Refactor jumbo string rewriting to outside application writer
Change-Id: I1173acd3a95b2929343a40d35d5c005ae45307fe
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 f55776f..36d1d90 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.dex.VirtualFile.FilePerInputClassDistributor;
import com.android.tools.r8.dex.VirtualFile.ItemUseInfo;
+import com.android.tools.r8.dex.jumbostrings.JumboStringRewriter;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
import com.android.tools.r8.graph.AppServices;
@@ -37,7 +38,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexWritableCode;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
@@ -163,8 +163,7 @@
public static ApplicationWriter create(
AppView<?> appView, Marker marker, DexIndexedConsumer consumer) {
- if (appView.options().getTestingOptions().forceDexContainerFormat
- || appView.options().canUseContainerDex()) {
+ if (appView.options().enableContainerDex()) {
if (!DexVersion.getDexVersion(appView.options().getMinApiLevel()).isContainerDex()) {
appView
.options()
@@ -281,28 +280,6 @@
write(executorService, null);
}
- protected Timing rewriteJumboStringsAndComputeDebugRepresentation(
- VirtualFile virtualFile, List<LazyDexString> lazyDexStrings) {
- Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
- computeOffsetMappingAndRewriteJumboStrings(virtualFile, lazyDexStrings, fileTiming);
- DebugRepresentation.computeForFile(appView, virtualFile);
- fileTiming.end();
- return fileTiming;
- }
-
- protected Collection<Timing> rewriteJumboStringsAndComputeDebugRepresentation(
- ExecutorService executorService,
- List<VirtualFile> virtualFiles,
- List<LazyDexString> lazyDexStrings)
- throws ExecutionException {
- return ThreadUtils.processItemsWithResults(
- virtualFiles,
- virtualFile ->
- rewriteJumboStringsAndComputeDebugRepresentation(virtualFile, lazyDexStrings),
- appView.options().getThreadingModule(),
- executorService);
- }
-
protected void writeVirtualFiles(
ExecutorService executorService,
List<VirtualFile> virtualFiles,
@@ -375,16 +352,8 @@
true);
new SortAnnotations(appView).run(executorService, timing);
-
- {
- // Compute offsets and rewrite jumbo strings so that code offsets are fixed.
- TimingMerger merger = timing.beginMerger("Pre-write phase", executorService);
- Collection<Timing> timings =
- rewriteJumboStringsAndComputeDebugRepresentation(
- executorService, virtualFiles, lazyDexStrings);
- merger.add(timings);
- merger.end();
- }
+ JumboStringRewriter.create(appView, lazyDexStrings, virtualFiles)
+ .run(executorService, timing);
// Now that the instruction offsets in each code object are fixed, compute the mapping file
// content.
@@ -544,19 +513,6 @@
return dexSourceFile;
}
- private void computeOffsetMappingAndRewriteJumboStrings(
- VirtualFile virtualFile, List<LazyDexString> lazyDexStrings, Timing timing) {
- if (virtualFile.isEmpty()) {
- return;
- }
- timing.begin("Compute object offset mapping");
- virtualFile.computeMapping(appView, lazyDexStrings.size(), timing);
- timing.end();
- timing.begin("Rewrite jumbo strings");
- rewriteCodeWithJumboStrings(virtualFile.getObjectMapping(), virtualFile.classes());
- timing.end();
- }
-
private <T extends DexItem> void printUse(Map<T, ItemUseInfo> useMap, String label) {
assert options.testing.calculateItemUseCountInDex;
List<IntBox> notMany = new ArrayList<>();
@@ -920,35 +876,6 @@
method -> method.getDefinition().getCode().asDexWritableCode().setCallSiteContexts(method));
}
- /**
- * Rewrites the code for all methods in the given file so that they use JumboString for at least
- * the strings that require it in mapping.
- *
- * <p>If run multiple times on a class, the lowest index that is required to be a JumboString will
- * be used.
- */
- protected void rewriteCodeWithJumboStrings(
- ObjectToOffsetMapping mapping, Collection<DexProgramClass> classes) {
- // Do not bail out early if forcing jumbo string processing.
- if (!options.testing.forceJumboStringProcessing) {
- // If there are no strings with jumbo indices at all this is a no-op.
- if (!mapping.hasJumboStrings()) {
- return;
- }
- }
- for (DexProgramClass clazz : classes) {
- clazz.forEachProgramMethodMatching(
- DexEncodedMethod::hasCode,
- method -> {
- DexWritableCode code = method.getDefinition().getCode().asDexWritableCode();
- DexWritableCode rewrittenCode =
- code.rewriteCodeWithJumboStrings(
- method, mapping, appView, options.testing.forceJumboStringProcessing);
- method.setCode(rewrittenCode.asCode(), appView);
- });
- }
- }
-
private ByteBufferResult writeDexFile(
ObjectToOffsetMapping objectMapping,
ByteBufferProvider provider,
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriterContainer.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriterContainer.java
index f9c3426..b69ed82 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriterContainer.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriterContainer.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.ProgramConsumer;
-import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.dex.FileWriter.DexContainerSection;
import com.android.tools.r8.dex.FileWriter.MapItem;
@@ -22,7 +21,6 @@
import com.android.tools.r8.utils.BitUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.timing.Timing;
import com.android.tools.r8.utils.timing.TimingMerger;
import com.google.common.collect.Sets;
@@ -31,7 +29,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
class ApplicationWriterContainer extends ApplicationWriter {
@@ -41,63 +38,6 @@
}
@Override
- protected Collection<Timing> rewriteJumboStringsAndComputeDebugRepresentation(
- ExecutorService executorService,
- List<VirtualFile> virtualFiles,
- List<LazyDexString> lazyDexStrings)
- throws ExecutionException {
- if (virtualFiles.isEmpty()) {
- return new ArrayList<>();
- }
- // Collect strings from all virtual files into the last DEX section.
- VirtualFile lastFile = virtualFiles.get(virtualFiles.size() - 1);
- List<VirtualFile> allExceptLastFile = virtualFiles.subList(0, virtualFiles.size() - 1);
- for (VirtualFile virtualFile : allExceptLastFile) {
- lastFile.indexedItems.addStrings(virtualFile.indexedItems.getStrings());
- }
- Collection<Timing> timings = new ArrayList<>(virtualFiles.size());
- // Compute string layout and handle jumbo strings for the last DEX section.
- timings.add(rewriteJumboStringsAndComputeDebugRepresentation(lastFile, lazyDexStrings));
- // Handle jumbo strings for the remaining DEX sections using the string ids in the last DEX
- // section.
- timings.addAll(
- ThreadUtils.processItemsWithResults(
- allExceptLastFile,
- virtualFile ->
- rewriteJumboStringsAndComputeDebugRepresentationWithExternalStringIds(
- virtualFile, lazyDexStrings, lastFile.getObjectMapping()),
- appView.options().getThreadingModule(),
- executorService));
- return timings;
- }
-
- private Timing rewriteJumboStringsAndComputeDebugRepresentationWithExternalStringIds(
- VirtualFile virtualFile, List<LazyDexString> lazyDexStrings, ObjectToOffsetMapping mapping) {
- Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
- computeOffsetMappingAndRewriteJumboStringsWithExternalStringIds(
- virtualFile, lazyDexStrings, fileTiming, mapping);
- DebugRepresentation.computeForFile(appView, virtualFile);
- fileTiming.end();
- return fileTiming;
- }
-
- private void computeOffsetMappingAndRewriteJumboStringsWithExternalStringIds(
- VirtualFile virtualFile,
- List<LazyDexString> lazyDexStrings,
- Timing timing,
- ObjectToOffsetMapping mapping) {
- if (virtualFile.isEmpty()) {
- return;
- }
- timing.begin("Compute object offset mapping");
- virtualFile.computeMapping(appView, lazyDexStrings.size(), timing, mapping);
- timing.end();
- timing.begin("Rewrite jumbo strings");
- rewriteCodeWithJumboStrings(virtualFile.getObjectMapping(), virtualFile.classes());
- timing.end();
- }
-
- @Override
protected void writeVirtualFiles(
ExecutorService executorService,
List<VirtualFile> virtualFiles,
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 608a129..96e5efc 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -753,7 +753,7 @@
return fields.size();
}
- Collection<DexString> getStrings() {
+ public Collection<DexString> getStrings() {
return strings;
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/jumbostrings/ContainerJumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/jumbostrings/ContainerJumboStringRewriter.java
new file mode 100644
index 0000000..f2fd227
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/jumbostrings/ContainerJumboStringRewriter.java
@@ -0,0 +1,76 @@
+// 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.dex.jumbostrings;
+
+import com.android.tools.r8.debuginfo.DebugRepresentation;
+import com.android.tools.r8.dex.ApplicationWriter.LazyDexString;
+import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.timing.Timing;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class ContainerJumboStringRewriter extends JumboStringRewriter {
+
+ public ContainerJumboStringRewriter(
+ AppView<?> appView, List<LazyDexString> lazyDexStrings, List<VirtualFile> virtualFiles) {
+ super(appView, lazyDexStrings, virtualFiles);
+ }
+
+ @Override
+ public Collection<Timing> processVirtualFiles(ExecutorService executorService)
+ throws ExecutionException {
+ if (virtualFiles.isEmpty()) {
+ return new ArrayList<>();
+ }
+ // Collect strings from all virtual files into the last DEX section.
+ VirtualFile lastFile = virtualFiles.get(virtualFiles.size() - 1);
+ List<VirtualFile> allExceptLastFile = virtualFiles.subList(0, virtualFiles.size() - 1);
+ for (VirtualFile virtualFile : allExceptLastFile) {
+ lastFile.indexedItems.addStrings(virtualFile.indexedItems.getStrings());
+ }
+ Collection<Timing> timings = new ArrayList<>(virtualFiles.size());
+ // Compute string layout and handle jumbo strings for the last DEX section.
+ timings.add(processVirtualFile(lastFile));
+ // Handle jumbo strings for the remaining DEX sections using the string ids in the last DEX
+ // section.
+ timings.addAll(
+ ThreadUtils.processItemsWithResults(
+ allExceptLastFile,
+ virtualFile ->
+ rewriteJumboStringsAndComputeDebugRepresentationWithExternalStringIds(
+ virtualFile, lastFile.getObjectMapping()),
+ appView.options().getThreadingModule(),
+ executorService));
+ return timings;
+ }
+
+ private Timing rewriteJumboStringsAndComputeDebugRepresentationWithExternalStringIds(
+ VirtualFile virtualFile, ObjectToOffsetMapping mapping) {
+ Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
+ computeOffsetMappingAndRewriteJumboStringsWithExternalStringIds(
+ virtualFile, fileTiming, mapping);
+ DebugRepresentation.computeForFile(appView, virtualFile);
+ fileTiming.end();
+ return fileTiming;
+ }
+
+ private void computeOffsetMappingAndRewriteJumboStringsWithExternalStringIds(
+ VirtualFile virtualFile, Timing timing, ObjectToOffsetMapping mapping) {
+ if (virtualFile.isEmpty()) {
+ return;
+ }
+ timing.begin("Compute object offset mapping");
+ virtualFile.computeMapping(appView, lazyDexStrings.size(), timing, mapping);
+ timing.end();
+ timing.begin("Rewrite jumbo strings");
+ rewriteCodeWithJumboStrings(virtualFile.getObjectMapping(), virtualFile.classes());
+ timing.end();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringCodeRewriter.java
similarity index 98%
rename from src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
rename to src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringCodeRewriter.java
index 1673d39..72585b5 100644
--- a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
+++ b/src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringCodeRewriter.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, 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.dex;
+package com.android.tools.r8.dex.jumbostrings;
import static com.android.tools.r8.graph.DexCode.TryHandler.NO_HANDLER;
import static com.android.tools.r8.graph.DexDebugEventBuilder.addDefaultEventWithAdvancePcIfNecessary;
@@ -57,7 +57,7 @@
import java.util.Map.Entry;
import java.util.function.BooleanSupplier;
-public class JumboStringRewriter {
+public class JumboStringCodeRewriter {
private static class TryTargets {
private DexInstruction start;
@@ -109,7 +109,7 @@
new Int2ReferenceOpenHashMap<>();
private final Map<TryHandler, List<DexInstruction>> handlerTargets = new IdentityHashMap<>();
- public JumboStringRewriter(
+ public JumboStringCodeRewriter(
DexEncodedMethod method,
DexString firstJumboString,
BooleanSupplier materializeInfoForNativePc,
diff --git a/src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringRewriter.java
new file mode 100644
index 0000000..292facf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/jumbostrings/JumboStringRewriter.java
@@ -0,0 +1,110 @@
+// 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.dex.jumbostrings;
+
+import com.android.tools.r8.debuginfo.DebugRepresentation;
+import com.android.tools.r8.dex.ApplicationWriter.LazyDexString;
+import com.android.tools.r8.dex.VirtualFile;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexWritableCode;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.timing.Timing;
+import com.android.tools.r8.utils.timing.TimingMerger;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class JumboStringRewriter {
+
+ protected final AppView<?> appView;
+ protected final InternalOptions options;
+ protected final List<VirtualFile> virtualFiles;
+ protected final List<LazyDexString> lazyDexStrings;
+
+ public JumboStringRewriter(
+ AppView<?> appView, List<LazyDexString> lazyDexStrings, List<VirtualFile> virtualFiles) {
+ this.appView = appView;
+ this.options = appView.options();
+ this.lazyDexStrings = lazyDexStrings;
+ this.virtualFiles = virtualFiles;
+ }
+
+ public static JumboStringRewriter create(
+ AppView<?> appView, List<LazyDexString> lazyDexStrings, List<VirtualFile> virtualFiles) {
+ return appView.options().enableContainerDex()
+ ? new ContainerJumboStringRewriter(appView, lazyDexStrings, virtualFiles)
+ : new JumboStringRewriter(appView, lazyDexStrings, virtualFiles);
+ }
+
+ public final void run(ExecutorService executorService, Timing timing) throws ExecutionException {
+ // Compute offsets and rewrite jumbo strings so that code offsets are fixed.
+ TimingMerger merger = timing.beginMerger("Pre-write phase", executorService);
+ Collection<Timing> timings = processVirtualFiles(executorService);
+ merger.add(timings);
+ merger.end();
+ }
+
+ protected Collection<Timing> processVirtualFiles(ExecutorService executorService)
+ throws ExecutionException {
+ return ThreadUtils.processItemsWithResults(
+ virtualFiles, this::processVirtualFile, options.getThreadingModule(), executorService);
+ }
+
+ protected final Timing processVirtualFile(VirtualFile virtualFile) {
+ Timing fileTiming = Timing.create("VirtualFile " + virtualFile.getId(), options);
+ computeOffsetMappingAndRewriteJumboStrings(virtualFile, fileTiming);
+ DebugRepresentation.computeForFile(appView, virtualFile);
+ fileTiming.end();
+ return fileTiming;
+ }
+
+ private void computeOffsetMappingAndRewriteJumboStrings(VirtualFile virtualFile, Timing timing) {
+ if (virtualFile.isEmpty()) {
+ return;
+ }
+ timing.begin("Compute object offset mapping");
+ virtualFile.computeMapping(appView, lazyDexStrings.size(), timing);
+ timing.end();
+ timing.begin("Rewrite jumbo strings");
+ rewriteCodeWithJumboStrings(virtualFile.getObjectMapping(), virtualFile.classes());
+ timing.end();
+ }
+
+ /**
+ * Rewrites the code for all methods in the given file so that they use JumboString for at least
+ * the strings that require it in mapping.
+ *
+ * <p>If run multiple times on a class, the lowest index that is required to be a JumboString will
+ * be used.
+ */
+ protected final void rewriteCodeWithJumboStrings(
+ ObjectToOffsetMapping mapping, Collection<DexProgramClass> classes) {
+ // Do not bail out early if forcing jumbo string processing.
+ if (!options.getTestingOptions().forceJumboStringProcessing) {
+ // If there are no strings with jumbo indices at all this is a no-op.
+ if (!mapping.hasJumboStrings()) {
+ return;
+ }
+ }
+ for (DexProgramClass clazz : classes) {
+ clazz.forEachProgramMethodMatching(
+ DexEncodedMethod::hasCode,
+ method -> {
+ DexWritableCode code = method.getDefinition().getCode().asDexWritableCode();
+ DexWritableCode rewrittenCode =
+ code.rewriteCodeWithJumboStrings(
+ method,
+ mapping,
+ appView,
+ options.getTestingOptions().forceJumboStringProcessing);
+ method.setCode(rewrittenCode.asCode(), appView);
+ });
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 79d0f24..04c65e4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -9,13 +9,13 @@
import com.android.tools.r8.dex.CodeToKeep;
import com.android.tools.r8.dex.IndexedItemCollection;
-import com.android.tools.r8.dex.JumboStringRewriter;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.dex.code.DexInstruction;
import com.android.tools.r8.dex.code.DexMonitorEnter;
import com.android.tools.r8.dex.code.DexReturnVoid;
import com.android.tools.r8.dex.code.DexSwitchPayload;
+import com.android.tools.r8.dex.jumbostrings.JumboStringCodeRewriter;
import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
import com.android.tools.r8.graph.DexDebugEvent.AdvanceLine;
import com.android.tools.r8.graph.DexDebugEvent.Default;
@@ -238,7 +238,7 @@
}
}
return firstJumboString != null
- ? new JumboStringRewriter(
+ ? new JumboStringCodeRewriter(
method.getDefinition(),
firstJumboString,
() -> appView.options().shouldMaterializeLineInfoForNativePcEncoding(method),
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 eead6a4..393b7ec 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2789,6 +2789,10 @@
return false;
}
+ public boolean enableContainerDex() {
+ return getTestingOptions().forceDexContainerFormat || canUseContainerDex();
+ }
+
public boolean canUseJavaLangVarHandleStoreStoreFence(DexDefinitionSupplier definitions) {
if (isGeneratingDex() && hasMinApi(AndroidApiLevel.T)) {
DexItemFactory factory = definitions.dexItemFactory();
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index 7bc5d20..f6a091a 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -158,6 +158,6 @@
.disableMethodNotNullCheck()
.disableAndroidApiLevelCheck()
.build();
- return new JumboStringRewriter(method, string, () -> false, factory).rewrite();
+ return new JumboStringCodeRewriter(method, string, () -> false, factory).rewrite();
}
}