Remove mutable local reference to DexApplication in R8.run
Bug: 149167169
Change-Id: I71dc90c16964921b615259503f1aa6c46ba9bc1b
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 26878af..0a033a3 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -118,6 +118,7 @@
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -268,6 +269,10 @@
new R8(options).run(app, executor);
}
+ private static DirectMappedDexApplication getDirectApp(AppView<?> appView) {
+ return appView.appInfo().app().asDirect();
+ }
+
private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException {
assert options.programConsumer != null;
if (options.quiet) {
@@ -279,14 +284,17 @@
"Running R8 version " + Version.LABEL + " with assertions enabled."));
}
try {
- DirectMappedDexApplication application =
- new ApplicationReader(inputApp, options, timing).read(executorService).toDirect();
+ AppView<AppInfoWithClassHierarchy> appView;
+ {
+ DirectMappedDexApplication application =
+ new ApplicationReader(inputApp, options, timing).read(executorService).toDirect();
- // Now that the dex-application is fully loaded, close any internal archive providers.
- inputApp.closeInternalArchiveProviders();
+ // Now that the dex-application is fully loaded, close any internal archive providers.
+ inputApp.closeInternalArchiveProviders();
- AppView<AppInfoWithClassHierarchy> appView = AppView.createForR8(application);
- appView.setAppServices(AppServices.builder(appView).build());
+ appView = AppView.createForR8(application);
+ appView.setAppServices(AppServices.builder(appView).build());
+ }
// Check for potentially having pass-through of Cf-code for kotlin libraries.
options.enableCfByteCodePassThrough =
@@ -302,7 +310,7 @@
InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
BackportedMethodRewriter.registerAssumedLibraryTypes(options);
if (options.enableEnumUnboxing) {
- if (application.definitionFor(options.itemFactory.enumUnboxingUtilityType) != null) {
+ if (appView.definitionFor(options.itemFactory.enumUnboxingUtilityType) != null) {
// The enum unboxing utility class can be created only during cf to dex compilation.
// If this is true, we are recompiling the dex application with R8 (compilation-steps).
options.enableEnumUnboxing = false;
@@ -317,7 +325,8 @@
Set<DexType> missingClasses = null;
try {
// TODO(b/154849103): Find a better way to determine missing classes.
- missingClasses = new SubtypingInfo(application.allClasses(), appView).getMissingClasses();
+ missingClasses =
+ new SubtypingInfo(getDirectApp(appView).allClasses(), appView).getMissingClasses();
missingClasses = filterMissingClasses(
missingClasses, options.getProguardConfiguration().getDontWarnPatterns());
if (!missingClasses.isEmpty()) {
@@ -350,7 +359,8 @@
options.itemFactory, AndroidApiLevel.getAndroidApiLevel(options.minApiLevel)));
}
}
- SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application);
+ SubtypingInfo subtypingInfo =
+ new SubtypingInfo(getDirectApp(appView).allClasses(), appView);
appView.setRootSet(
new RootSetBuilder(
appView,
@@ -363,7 +373,6 @@
options.isShrinking() ? AnnotationRemover.builder() : null;
AppView<AppInfoWithLiveness> appViewWithLiveness =
runEnqueuer(annotationRemoverBuilder, executorService, appView, subtypingInfo);
- application = appViewWithLiveness.appInfo().app().asDirect();
assert appView.rootSet().verifyKeptFieldsAreAccessedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness.appInfo());
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness.appInfo());
@@ -391,15 +400,12 @@
shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING));
TreePruner pruner = new TreePruner(appViewWithLiveness);
- application = pruner.run(application);
+ DirectMappedDexApplication prunedApp = pruner.run();
if (options.enableEnumUnboxing) {
DexProgramClass utilityClass =
EnumUnboxingRewriter.synthesizeEmptyEnumUnboxingUtilityClass(appView);
- // We cannot know at this point if the class will be on the main dex list,
- // updated later. Since this is inserted in the app at this point, we do not need
- // to use any synthesized class hack and add the class as a program class.
- application = application.builder().addProgramClass(utilityClass).build();
+ prunedApp = prunedApp.builder().addProgramClass(utilityClass).build();
}
// Recompute the subtyping information.
@@ -409,7 +415,7 @@
.appInfo()
.withLiveness()
.prunedCopyFrom(
- application,
+ prunedApp,
removedClasses,
pruner.getMethodsToKeepForConfigurationDebugging()));
appView.setAppServices(appView.appServices().prunedCopy(removedClasses));
@@ -430,7 +436,7 @@
}
assert appView.appInfo().hasLiveness();
- assert verifyNoJarApplicationReaders(application.classes());
+ assert verifyNoJarApplicationReaders(appView.appInfo().classes());
// Build conservative main dex content after first round of tree shaking. This is used
// by certain optimizations to avoid introducing additional class references into main dex
// classes, as that can cause the final number of main dex methods to grow.
@@ -440,7 +446,7 @@
assert appView.graphLens().isIdentityLens();
// Find classes which may have code executed before secondary dex files installation.
SubtypingInfo subtypingInfo =
- new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView);
+ new SubtypingInfo(getDirectApp(appView).allClasses(), appView);
mainDexRootSet =
new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules)
.run(executorService);
@@ -449,7 +455,7 @@
EnqueuerFactory.createForMainDexTracing(appView, subtypingInfo)
.traceMainDex(mainDexRootSet, executorService, timing);
// Calculate the automatic main dex list according to legacy multidex constraints.
- mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
+ mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, getDirectApp(appView)).run();
appView.appInfo().unsetObsolete();
}
@@ -463,7 +469,11 @@
SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo();
GraphLens publicizedLens =
ClassAndMemberPublicizer.run(
- executorService, timing, application, appViewWithLiveness, subtypingInfo);
+ executorService,
+ timing,
+ appViewWithLiveness.appInfo().app(),
+ appViewWithLiveness,
+ subtypingInfo);
boolean changed = appView.setGraphLens(publicizedLens);
if (changed) {
// We can now remove visibility bridges. Note that we do not need to update the
@@ -480,12 +490,13 @@
if (options.shouldDesugarNests()) {
timing.begin("NestBasedAccessDesugaring");
R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness);
- NestedPrivateMethodLens lens = analyzer.run(executorService, application.builder());
+ NestedPrivateMethodLens lens =
+ analyzer.run(executorService, getDirectApp(appView).builder());
if (lens != null) {
boolean changed = appView.setGraphLens(lens);
assert changed;
appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ appViewWithLiveness.appInfo().rewrittenWithLens(getDirectApp(appView), lens));
}
timing.end();
} else {
@@ -512,7 +523,7 @@
boolean changed = appView.setGraphLens(lens);
assert changed;
appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ appViewWithLiveness.appInfo().rewrittenWithLens(getDirectApp(appView), lens));
}
timing.end();
}
@@ -520,15 +531,19 @@
timing.begin("VerticalClassMerger");
VerticalClassMerger verticalClassMerger =
new VerticalClassMerger(
- application, appViewWithLiveness, executorService, timing, mainDexClasses);
+ getDirectApp(appViewWithLiveness),
+ appViewWithLiveness,
+ executorService,
+ timing,
+ mainDexClasses);
VerticalClassMergerGraphLens lens = verticalClassMerger.run();
if (lens != null) {
boolean changed = appView.setGraphLens(lens);
assert changed;
appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses());
- application = application.asDirect().rewrittenWithLens(lens);
+ DirectMappedDexApplication application = getDirectApp(appView).rewrittenWithLens(lens);
appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ appViewWithLiveness.appInfo().rewrittenWithLens(application, lens));
}
timing.end();
}
@@ -544,9 +559,9 @@
if (lens != null) {
boolean changed = appView.setGraphLens(lens);
assert changed;
- assert application.asDirect().verifyNothingToRewrite(appView, lens);
+ assert getDirectApp(appView).verifyNothingToRewrite(appView, lens);
appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ appViewWithLiveness.appInfo().rewrittenWithLens(getDirectApp(appView), lens));
}
timing.end();
}
@@ -562,9 +577,9 @@
if (lens != null) {
boolean changed = appView.setGraphLens(lens);
assert changed;
- assert application.asDirect().verifyNothingToRewrite(appView, lens);
+ assert getDirectApp(appView).verifyNothingToRewrite(appView, lens);
appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens));
+ appViewWithLiveness.appInfo().rewrittenWithLens(getDirectApp(appView), lens));
}
timing.end();
}
@@ -584,11 +599,20 @@
appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLens()));
+ // Collect the already pruned types before creating a new app info without liveness.
+ // TODO: we should avoid removing liveness.
+ Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
+
+ // TODO: move to appview.
+ EnumValueInfoMapCollection enumValueInfoMapCollection =
+ appViewWithLiveness.appInfo().getEnumValueInfoMapCollection();
+
timing.begin("Create IR");
CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
try {
IRConverter converter = new IRConverter(appView, timing, printer, mainDexClasses);
- application = converter.optimize(executorService).asDirect();
+ DexApplication application = converter.optimize(executorService).asDirect();
+ appView.setAppInfo(appView.appInfo().rebuild(previous -> application));
} finally {
timing.end();
}
@@ -600,7 +624,7 @@
// graph lens entirely, though, since it is needed for mapping all field and method signatures
// back to the original program.
timing.begin("AppliedGraphLens construction");
- appView.setGraphLens(new AppliedGraphLens(appView, application.classes()));
+ appView.setGraphLens(new AppliedGraphLens(appView, appView.appInfo().app().classes()));
timing.end();
if (options.printCfg) {
@@ -615,15 +639,7 @@
}
}
- // Collect the already pruned types before creating a new app info without liveness.
- Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
-
- // TODO: move to appview.
- EnumValueInfoMapCollection enumValueInfoMapCollection =
- appViewWithLiveness.appInfo().getEnumValueInfoMapCollection();
-
if (!options.mainDexKeepRules.isEmpty()) {
- appView.setAppInfo(new AppInfoWithClassHierarchy(application));
// No need to build a new main dex root set
assert mainDexRootSet != null;
GraphConsumer mainDexKeptGraphConsumer = options.mainDexKeptGraphConsumer;
@@ -636,14 +652,14 @@
Enqueuer enqueuer =
EnqueuerFactory.createForMainDexTracing(
appView,
- new SubtypingInfo(application.allClasses(), application),
+ new SubtypingInfo(getDirectApp(appView).allClasses(), appView),
mainDexKeptGraphConsumer);
// Find classes which may have code executed before secondary dex files installation.
// Live types is the tracing result.
Set<DexProgramClass> mainDexBaseClasses =
enqueuer.traceMainDex(mainDexRootSet, executorService, timing);
// Calculate the automatic main dex list according to legacy multidex constraints.
- mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, application).run();
+ mainDexClasses = new MainDexListBuilder(mainDexBaseClasses, getDirectApp(appView)).run();
final MainDexClasses finalMainDexClasses = mainDexClasses;
processWhyAreYouKeepingAndCheckDiscarded(
@@ -670,8 +686,6 @@
executorService);
}
- appView.setAppInfo(new AppInfoWithClassHierarchy(application));
-
if (options.shouldRerunEnqueuer()) {
timing.begin("Post optimization code stripping");
try {
@@ -688,7 +702,7 @@
Enqueuer enqueuer =
EnqueuerFactory.createForFinalTreeShaking(
appView,
- new SubtypingInfo(application.allClasses(), application),
+ new SubtypingInfo(getDirectApp(appView).allClasses(), appView),
keptGraphConsumer,
missingClasses,
prunedTypes);
@@ -716,7 +730,7 @@
DefaultTreePrunerConfiguration.getInstance());
TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
- application = pruner.run(application);
+ DirectMappedDexApplication application = pruner.run();
Set<DexType> removedClasses = pruner.getRemovedClasses();
if (options.usageInformationConsumer != null) {
@@ -788,12 +802,6 @@
}
}
- // Add automatic main dex classes to an eventual manual list of classes.
- if (!options.mainDexKeepRules.isEmpty()) {
- application =
- application.builder().addToMainDexList(mainDexClasses.getClasses()).build().asDirect();
- }
-
// Perform minification.
NamingLens namingLens;
if (options.getProguardConfiguration().hasApplyMappingFile()) {
@@ -817,19 +825,19 @@
new KotlinMetadataRewriter(appView, namingLens).run(executorService);
timing.end();
- assert verifyMovedMethodsHaveOriginalMethodPosition(appView, application);
+ assert verifyMovedMethodsHaveOriginalMethodPosition(appView, getDirectApp(appView));
timing.begin("Line number remapping");
// When line number optimization is turned off the identity mapping for line numbers is
// used. We still run the line number optimizer to collect line numbers and inline frame
// information for the mapping file.
ClassNameMapper classNameMapper =
- LineNumberOptimizer.run(appView, application, inputApp, namingLens);
+ LineNumberOptimizer.run(appView, getDirectApp(appView), inputApp, namingLens);
timing.end();
// Overwrite SourceFile if specified. This step should be done after IR conversion.
timing.begin("Rename SourceFile");
- new SourceFileRewriter(appView, application).run();
+ new SourceFileRewriter(appView, appView.appInfo().app()).run();
timing.end();
// If a method filter is present don't produce output since the application is likely partial.
@@ -851,18 +859,33 @@
assert !options.isShrinking();
}
+ // Add automatic main dex classes to an eventual manual list of classes.
+ if (!options.mainDexKeepRules.isEmpty()) {
+ MainDexClasses finalMainDexClasses = mainDexClasses;
+ appView.setAppInfo(
+ appView
+ .appInfo()
+ .rebuild(
+ application ->
+ application
+ .builder()
+ .addToMainDexList(finalMainDexClasses.getClasses())
+ .build()
+ .asDirect()));
+ }
+
// Validity checks.
- assert application.asDirect().verifyCodeObjectsOwners();
- assert application.classes().stream().allMatch(clazz -> clazz.isValid(options));
+ assert getDirectApp(appView).verifyCodeObjectsOwners();
+ assert appView.appInfo().classes().stream().allMatch(clazz -> clazz.isValid(options));
if (options.isShrinking()
|| options.isMinifying()
|| options.getProguardConfiguration().hasApplyMappingFile()) {
- assert appView.rootSet().verifyKeptItemsAreKept(application, appView.appInfo());
+ assert appView.rootSet().verifyKeptItemsAreKept(appView.appInfo().app(), appView.appInfo());
}
assert appView
.graphLens()
.verifyMappingToOriginalProgram(
- application.classesWithDeterministicOrder(),
+ appView.appInfo().classesWithDeterministicOrder(),
new ApplicationReader(inputApp.withoutMainDexList(), options, timing)
.read(executorService),
appView.dexItemFactory());
@@ -882,7 +905,7 @@
// Generate the resulting application resources.
writeApplication(
executorService,
- application,
+ appView.appInfo().app(),
appView,
appView.graphLens(),
appView.initClassLens(),
@@ -1058,7 +1081,7 @@
throw new CompilationError("Discard checks failed.");
}
- private static boolean verifyNoJarApplicationReaders(List<DexProgramClass> classes) {
+ private static boolean verifyNoJarApplicationReaders(Collection<DexProgramClass> classes) {
for (DexProgramClass clazz : classes) {
for (DexEncodedMethod method : clazz.methods()) {
if (method.getCode() != null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index ec2ced1..be956c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -57,7 +57,8 @@
: UsagePrinter.DONT_PRINT;
}
- public DirectMappedDexApplication run(DirectMappedDexApplication application) {
+ public DirectMappedDexApplication run() {
+ DirectMappedDexApplication application = appView.appInfo().app().asDirect();
Timing timing = application.timing;
timing.begin("Pruning application...");
DirectMappedDexApplication result;