| // Copyright (c) 2016, 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; | 
 |  | 
 | import static com.android.tools.r8.R8Command.USAGE_MESSAGE; | 
 | import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException; | 
 |  | 
 | import com.android.tools.r8.dex.ApplicationReader; | 
 | import com.android.tools.r8.dex.ApplicationWriter; | 
 | import com.android.tools.r8.dex.Marker; | 
 | import com.android.tools.r8.dex.Marker.Tool; | 
 | import com.android.tools.r8.errors.CompilationError; | 
 | import com.android.tools.r8.experimental.graphinfo.GraphConsumer; | 
 | import com.android.tools.r8.graph.AppInfoWithClassHierarchy; | 
 | import com.android.tools.r8.graph.AppServices; | 
 | import com.android.tools.r8.graph.AppView; | 
 | import com.android.tools.r8.graph.AppliedGraphLens; | 
 | import com.android.tools.r8.graph.DexApplication; | 
 | import com.android.tools.r8.graph.DexClass; | 
 | import com.android.tools.r8.graph.DexDefinition; | 
 | import com.android.tools.r8.graph.DexEncodedMethod; | 
 | import com.android.tools.r8.graph.DexProgramClass; | 
 | import com.android.tools.r8.graph.DexReference; | 
 | import com.android.tools.r8.graph.DexType; | 
 | import com.android.tools.r8.graph.DirectMappedDexApplication; | 
 | import com.android.tools.r8.graph.EnumValueInfoMapCollection; | 
 | import com.android.tools.r8.graph.GraphLense; | 
 | import com.android.tools.r8.graph.GraphLense.NestedGraphLense; | 
 | import com.android.tools.r8.graph.InitClassLens; | 
 | import com.android.tools.r8.graph.SubtypingInfo; | 
 | import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis; | 
 | import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis; | 
 | import com.android.tools.r8.inspector.internal.InspectorImpl; | 
 | import com.android.tools.r8.ir.conversion.IRConverter; | 
 | import com.android.tools.r8.ir.desugar.BackportedMethodRewriter; | 
 | import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter; | 
 | import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter; | 
 | import com.android.tools.r8.ir.desugar.NestedPrivateMethodLense; | 
 | import com.android.tools.r8.ir.desugar.R8NestBasedAccessDesugaring; | 
 | import com.android.tools.r8.ir.optimize.AssertionsRewriter; | 
 | import com.android.tools.r8.ir.optimize.MethodPoolCollection; | 
 | import com.android.tools.r8.ir.optimize.NestReducer; | 
 | import com.android.tools.r8.ir.optimize.SwitchMapCollector; | 
 | import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization; | 
 | import com.android.tools.r8.ir.optimize.UninstantiatedTypeOptimization.UninstantiatedTypeOptimizationGraphLense; | 
 | import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector; | 
 | import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLense; | 
 | import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods; | 
 | import com.android.tools.r8.ir.optimize.enums.EnumUnboxingRewriter; | 
 | import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector; | 
 | import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; | 
 | 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.ClassNameMapper; | 
 | 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.ProguardMapSupplier; | 
 | import com.android.tools.r8.naming.SeedMapper; | 
 | import com.android.tools.r8.naming.SourceFileRewriter; | 
 | import com.android.tools.r8.naming.signature.GenericSignatureRewriter; | 
 | import com.android.tools.r8.optimize.BridgeHoisting; | 
 | import com.android.tools.r8.optimize.ClassAndMemberPublicizer; | 
 | import com.android.tools.r8.optimize.MemberRebindingAnalysis; | 
 | import com.android.tools.r8.optimize.VisibilityBridgeRemover; | 
 | import com.android.tools.r8.origin.CommandLineOrigin; | 
 | import com.android.tools.r8.shaking.AbstractMethodRemover; | 
 | import com.android.tools.r8.shaking.AnnotationRemover; | 
 | import com.android.tools.r8.shaking.AppInfoWithLiveness; | 
 | import com.android.tools.r8.shaking.ClassInitFieldSynthesizer; | 
 | import com.android.tools.r8.shaking.DefaultTreePrunerConfiguration; | 
 | import com.android.tools.r8.shaking.DiscardedChecker; | 
 | import com.android.tools.r8.shaking.Enqueuer; | 
 | import com.android.tools.r8.shaking.Enqueuer.Mode; | 
 | import com.android.tools.r8.shaking.EnqueuerFactory; | 
 | import com.android.tools.r8.shaking.MainDexClasses; | 
 | import com.android.tools.r8.shaking.MainDexListBuilder; | 
 | import com.android.tools.r8.shaking.ProguardClassFilter; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationRule; | 
 | import com.android.tools.r8.shaking.ProguardConfigurationUtils; | 
 | import com.android.tools.r8.shaking.RootSetBuilder; | 
 | import com.android.tools.r8.shaking.RootSetBuilder.RootSet; | 
 | import com.android.tools.r8.shaking.StaticClassMerger; | 
 | import com.android.tools.r8.shaking.TreePruner; | 
 | import com.android.tools.r8.shaking.TreePrunerConfiguration; | 
 | import com.android.tools.r8.shaking.VerticalClassMerger; | 
 | import com.android.tools.r8.shaking.VerticalClassMergerGraphLense; | 
 | import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer; | 
 | import com.android.tools.r8.utils.AndroidApiLevel; | 
 | import com.android.tools.r8.utils.AndroidApp; | 
 | import com.android.tools.r8.utils.CfgPrinter; | 
 | import com.android.tools.r8.utils.CollectionUtils; | 
 | import com.android.tools.r8.utils.ExceptionUtils; | 
 | import com.android.tools.r8.utils.InternalOptions; | 
 | import com.android.tools.r8.utils.LineNumberOptimizer; | 
 | import com.android.tools.r8.utils.SelfRetraceTest; | 
 | import com.android.tools.r8.utils.StringDiagnostic; | 
 | import com.android.tools.r8.utils.ThreadUtils; | 
 | import com.android.tools.r8.utils.Timing; | 
 | import com.google.common.collect.ImmutableList; | 
 | import com.google.common.collect.Iterables; | 
 | import com.google.common.collect.Sets; | 
 | import com.google.common.io.ByteStreams; | 
 | import java.io.ByteArrayOutputStream; | 
 | import java.io.FileOutputStream; | 
 | import java.io.IOException; | 
 | import java.io.OutputStreamWriter; | 
 | import java.io.PrintStream; | 
 | import java.nio.charset.StandardCharsets; | 
 | import java.util.ArrayList; | 
 | import java.util.HashSet; | 
 | import java.util.List; | 
 | import java.util.Set; | 
 | import java.util.concurrent.ExecutionException; | 
 | import java.util.concurrent.ExecutorService; | 
 | import java.util.function.Supplier; | 
 |  | 
 | /** | 
 |  * The R8 compiler. | 
 |  * | 
 |  * <p>R8 performs whole-program optimizing compilation of Java bytecode. It supports compilation of | 
 |  * Java bytecode to Java bytecode or DEX bytecode. R8 supports tree-shaking the program to remove | 
 |  * unneeded code and it supports minification of the program names to reduce the size of the | 
 |  * resulting program. | 
 |  * | 
 |  * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this | 
 |  * API does not suffice please contact the D8/R8 team. | 
 |  * | 
 |  * <p>R8 supports some configuration using configuration files mostly compatible with the format of | 
 |  * the <a href="https://www.guardsquare.com/en/proguard">ProGuard</a> optimizer. | 
 |  * | 
 |  * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {link | 
 |  * R8Command}. For example: | 
 |  * | 
 |  * <pre> | 
 |  *   R8.run(R8Command.builder() | 
 |  *       .addProgramFiles(inputPathA, inputPathB) | 
 |  *       .addLibraryFiles(androidJar) | 
 |  *       .setOutput(outputPath, OutputMode.DexIndexed) | 
 |  *       .build()); | 
 |  * </pre> | 
 |  * | 
 |  * The above reads the input files denoted by {@code inputPathA} and {@code inputPathB}, compiles | 
 |  * them to DEX bytecode, using {@code androidJar} as the reference of the system runtime library, | 
 |  * and then writes the result to the directory or zip archive specified by {@code outputPath}. | 
 |  */ | 
 | @Keep | 
 | public class R8 { | 
 |  | 
 |   private final Timing timing; | 
 |   private final InternalOptions options; | 
 |  | 
 |   private R8(InternalOptions options) { | 
 |     this.options = options; | 
 |     if (options.printMemory) { | 
 |       System.gc(); | 
 |     } | 
 |     timing = Timing.create("R8", options); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Main API entry for the R8 compiler. | 
 |    * | 
 |    * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this | 
 |    * API does not suffice please contact the R8 team. | 
 |    * | 
 |    * @param command R8 command. | 
 |    */ | 
 |   public static void run(R8Command command) throws CompilationFailedException { | 
 |     AndroidApp app = command.getInputApp(); | 
 |     InternalOptions options = command.getInternalOptions(); | 
 |     runForTesting(app, options); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Main API entry for the R8 compiler. | 
 |    * | 
 |    * <p>The R8 API is intentionally limited and should "do the right thing" given a command. If this | 
 |    * API does not suffice please contact the R8 team. | 
 |    * | 
 |    * @param command R8 command. | 
 |    * @param executor executor service from which to get threads for multi-threaded processing. | 
 |    */ | 
 |   public static void run(R8Command command, ExecutorService executor) | 
 |       throws CompilationFailedException { | 
 |     AndroidApp app = command.getInputApp(); | 
 |     InternalOptions options = command.getInternalOptions(); | 
 |     ExceptionUtils.withR8CompilationHandler( | 
 |         command.getReporter(), | 
 |         () -> { | 
 |           run(app, options, executor); | 
 |         }); | 
 |   } | 
 |  | 
 |   static void writeApplication( | 
 |       ExecutorService executorService, | 
 |       DexApplication application, | 
 |       AppView<?> appView, | 
 |       GraphLense graphLense, | 
 |       InitClassLens initClassLens, | 
 |       NamingLens namingLens, | 
 |       InternalOptions options, | 
 |       ProguardMapSupplier proguardMapSupplier) | 
 |       throws ExecutionException { | 
 |     InspectorImpl.runInspections(options.outputInspections, application); | 
 |     try { | 
 |       Marker marker = options.getMarker(Tool.R8); | 
 |       assert marker != null; | 
 |       // Get the markers from the input which are different from the one created for this | 
 |       // compilation | 
 |       Set<Marker> markers = new HashSet<>(options.itemFactory.extractMarkers()); | 
 |       markers.remove(marker); | 
 |       if (options.isGeneratingClassFiles()) { | 
 |         new CfApplicationWriter( | 
 |                 application, appView, options, marker, graphLense, namingLens, proguardMapSupplier) | 
 |             .write(options.getClassFileConsumer()); | 
 |       } else { | 
 |         new ApplicationWriter( | 
 |                 application, | 
 |                 appView, | 
 |                 options, | 
 |                 // Ensure that the marker for this compilation is the first in the list. | 
 |                 ImmutableList.<Marker>builder().add(marker).addAll(markers).build(), | 
 |                 graphLense, | 
 |                 initClassLens, | 
 |                 namingLens, | 
 |                 proguardMapSupplier) | 
 |             .write(executorService); | 
 |       } | 
 |     } catch (IOException e) { | 
 |       throw new RuntimeException("Cannot write application", e); | 
 |     } | 
 |   } | 
 |  | 
 |   private Set<DexType> filterMissingClasses(Set<DexType> missingClasses, | 
 |       ProguardClassFilter dontWarnPatterns) { | 
 |     Set<DexType> result = new HashSet<>(missingClasses); | 
 |     dontWarnPatterns.filterOutMatches(result); | 
 |     return result; | 
 |   } | 
 |  | 
 |   static void runForTesting(AndroidApp app, InternalOptions options) | 
 |       throws CompilationFailedException { | 
 |     ExecutorService executor = ThreadUtils.getExecutorService(options); | 
 |     ExceptionUtils.withR8CompilationHandler( | 
 |         options.reporter, | 
 |         () -> { | 
 |           try { | 
 |             run(app, options, executor); | 
 |           } finally { | 
 |             executor.shutdown(); | 
 |           } | 
 |         }); | 
 |   } | 
 |  | 
 |   private static void run(AndroidApp app, InternalOptions options, ExecutorService executor) | 
 |       throws IOException { | 
 |     new R8(options).run(app, executor); | 
 |   } | 
 |  | 
 |   private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException { | 
 |     assert options.programConsumer != null; | 
 |     if (options.quiet) { | 
 |       System.setOut(new PrintStream(ByteStreams.nullOutputStream())); | 
 |     } | 
 |     if (this.getClass().desiredAssertionStatus()) { | 
 |       options.reporter.info( | 
 |           new StringDiagnostic( | 
 |               "Running R8 version " + Version.LABEL + " with assertions enabled.")); | 
 |     } | 
 |     try { | 
 |       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(); | 
 |  | 
 |       AppView<AppInfoWithClassHierarchy> appView = | 
 |           AppView.createForR8(new AppInfoWithClassHierarchy(application), options); | 
 |       appView.setAppServices(AppServices.builder(appView).build()); | 
 |  | 
 |       // Check for potentially having pass-through of Cf-code for kotlin libraries. | 
 |       options.enableCfByteCodePassThrough = | 
 |           options.isGeneratingClassFiles() && KotlinMetadataUtils.mayProcessKotlinMetadata(appView); | 
 |  | 
 |       // Up-front check for valid library setup. | 
 |       if (!options.mainDexKeepRules.isEmpty()) { | 
 |         MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo()); | 
 |       } | 
 |       if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) { | 
 |         DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView); | 
 |         application = DesugaredLibraryRetargeter.amendLibraryWithRetargetedMembers(appView); | 
 |       } | 
 |       InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options); | 
 |       BackportedMethodRewriter.registerAssumedLibraryTypes(options); | 
 |       if (options.enableEnumUnboxing) { | 
 |         if (application.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; | 
 |         } else { | 
 |           EnumUnboxingCfMethods.registerSynthesizedCodeReferences(options.itemFactory); | 
 |         } | 
 |       } | 
 |  | 
 |       List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>(); | 
 |       timing.begin("Strip unused code"); | 
 |       Set<DexType> classesToRetainInnerClassAttributeFor = null; | 
 |       Set<DexType> missingClasses = null; | 
 |       try { | 
 |         // TODO(b/154849103): Find a better way to determine missing classes. | 
 |         missingClasses = new SubtypingInfo(application.allClasses(), appView).getMissingClasses(); | 
 |         missingClasses = filterMissingClasses( | 
 |             missingClasses, options.getProguardConfiguration().getDontWarnPatterns()); | 
 |         if (!missingClasses.isEmpty()) { | 
 |           missingClasses.forEach( | 
 |               clazz -> { | 
 |                 options.reporter.warning( | 
 |                     new StringDiagnostic("Missing class: " + clazz.toSourceString())); | 
 |               }); | 
 |           if (!options.ignoreMissingClasses) { | 
 |             DexType missingClass = missingClasses.iterator().next(); | 
 |             if (missingClasses.size() == 1) { | 
 |               throw new CompilationError( | 
 |                   "Compilation can't be completed because the class `" | 
 |                       + missingClass.toSourceString() | 
 |                       + "` is missing."); | 
 |             } else { | 
 |               throw new CompilationError( | 
 |                   "Compilation can't be completed because `" + missingClass.toSourceString() | 
 |                       + "` and " + (missingClasses.size() - 1) + " other classes are missing."); | 
 |             } | 
 |           } | 
 |         } | 
 |  | 
 |         // Add synthesized -assumenosideeffects from min api if relevant. | 
 |         if (options.isGeneratingDex()) { | 
 |           if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk( | 
 |               options.itemFactory, options.getProguardConfiguration().getRules())) { | 
 |             synthesizedProguardRules.add( | 
 |                 ProguardConfigurationUtils.buildAssumeNoSideEffectsRuleForApiLevel( | 
 |                     options.itemFactory, AndroidApiLevel.getAndroidApiLevel(options.minApiLevel))); | 
 |           } | 
 |         } | 
 |         SubtypingInfo subtypingInfo = new SubtypingInfo(application.allClasses(), application); | 
 |         appView.setRootSet( | 
 |             new RootSetBuilder( | 
 |                     appView, | 
 |                     subtypingInfo, | 
 |                     Iterables.concat( | 
 |                         options.getProguardConfiguration().getRules(), synthesizedProguardRules)) | 
 |                 .run(executorService)); | 
 |  | 
 |         AnnotationRemover.Builder annotationRemoverBuilder = | 
 |             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()); | 
 |         assert appView.rootSet().verifyKeptItemsAreKept(appView.appInfo().app(), appView.appInfo()); | 
 |  | 
 |         missingClasses = | 
 |             Sets.union(missingClasses, appViewWithLiveness.appInfo().getMissingTypes()); | 
 |  | 
 |         appView.rootSet().checkAllRulesAreUsed(options); | 
 |  | 
 |         if (options.proguardSeedsConsumer != null) { | 
 |           ByteArrayOutputStream bytes = new ByteArrayOutputStream(); | 
 |           PrintStream out = new PrintStream(bytes); | 
 |           RootSetBuilder.writeSeeds(appView.appInfo().withLiveness(), out, type -> true); | 
 |           out.flush(); | 
 |           ExceptionUtils.withConsumeResourceHandler( | 
 |               options.reporter, options.proguardSeedsConsumer, bytes.toString()); | 
 |           ExceptionUtils.withFinishedResourceHandler( | 
 |               options.reporter, options.proguardSeedsConsumer); | 
 |         } | 
 |         if (options.isShrinking()) { | 
 |           // Mark dead proto extensions fields as neither being read nor written. This step must | 
 |           // run prior to the tree pruner. | 
 |           appView.withGeneratedExtensionRegistryShrinker( | 
 |               shrinker -> shrinker.run(Mode.INITIAL_TREE_SHAKING)); | 
 |  | 
 |           TreePruner pruner = new TreePruner(appViewWithLiveness); | 
 |           application = pruner.run(application); | 
 |  | 
 |           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(); | 
 |           } | 
 |  | 
 |           // Recompute the subtyping information. | 
 |           Set<DexType> removedClasses = pruner.getRemovedClasses(); | 
 |           appView.setAppInfo( | 
 |               appView | 
 |                   .appInfo() | 
 |                   .withLiveness() | 
 |                   .prunedCopyFrom( | 
 |                       application, | 
 |                       removedClasses, | 
 |                       pruner.getMethodsToKeepForConfigurationDebugging())); | 
 |           appView.setAppServices(appView.appServices().prunedCopy(removedClasses)); | 
 |           new AbstractMethodRemover( | 
 |                   appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo()) | 
 |               .run(); | 
 |  | 
 |           AnnotationRemover annotationRemover = | 
 |               annotationRemoverBuilder | 
 |                   .computeClassesToRetainInnerClassAttributeFor(appViewWithLiveness) | 
 |                   .build(appViewWithLiveness, removedClasses); | 
 |           annotationRemover.ensureValid().run(); | 
 |           classesToRetainInnerClassAttributeFor = | 
 |               annotationRemover.getClassesToRetainInnerClassAttributeFor(); | 
 |         } | 
 |       } finally { | 
 |         timing.end(); | 
 |       } | 
 |  | 
 |       assert appView.appInfo().hasLiveness(); | 
 |       assert verifyNoJarApplicationReaders(application.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. | 
 |       RootSet mainDexRootSet = null; | 
 |       MainDexClasses mainDexClasses = MainDexClasses.NONE; | 
 |       if (!options.mainDexKeepRules.isEmpty()) { | 
 |         assert appView.graphLense().isIdentityLense(); | 
 |         // Find classes which may have code executed before secondary dex files installation. | 
 |         SubtypingInfo subtypingInfo = | 
 |             new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView); | 
 |         mainDexRootSet = | 
 |             new RootSetBuilder(appView, subtypingInfo, options.mainDexKeepRules) | 
 |                 .run(executorService); | 
 |         // Live types is the tracing result. | 
 |         Set<DexProgramClass> mainDexBaseClasses = | 
 |             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(); | 
 |         appView.appInfo().unsetObsolete(); | 
 |       } | 
 |  | 
 |       // The class type lattice elements include information about the interfaces that a class | 
 |       // implements. This information can change as a result of vertical class merging, so we need | 
 |       // to clear the cache, so that we will recompute the type lattice elements. | 
 |       appView.dexItemFactory().clearTypeElementsCache(); | 
 |  | 
 |       if (options.getProguardConfiguration().isAccessModificationAllowed()) { | 
 |         AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness(); | 
 |         SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo(); | 
 |         GraphLense publicizedLense = | 
 |             ClassAndMemberPublicizer.run( | 
 |                 executorService, timing, application, appViewWithLiveness, subtypingInfo); | 
 |         boolean changed = appView.setGraphLense(publicizedLense); | 
 |         if (changed) { | 
 |           // We can now remove visibility bridges. Note that we do not need to update the | 
 |           // invoke-targets here, as the existing invokes will simply dispatch to the now | 
 |           // visible super-method. MemberRebinding, if run, will then dispatch it correctly. | 
 |           new VisibilityBridgeRemover(appView.withLiveness()).run(); | 
 |         } | 
 |       } | 
 |  | 
 |       AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness(); | 
 |       appView.setGraphLense(new MemberRebindingAnalysis(appViewWithLiveness).run()); | 
 |       appView.appInfo().withLiveness().getFieldAccessInfoCollection().restrictToProgram(appView); | 
 |  | 
 |       if (options.shouldDesugarNests()) { | 
 |         timing.begin("NestBasedAccessDesugaring"); | 
 |         R8NestBasedAccessDesugaring analyzer = new R8NestBasedAccessDesugaring(appViewWithLiveness); | 
 |         NestedPrivateMethodLense lens = analyzer.run(executorService, application.builder()); | 
 |         if (lens != null) { | 
 |           boolean changed = appView.setGraphLense(lens); | 
 |           assert changed; | 
 |           appViewWithLiveness.setAppInfo( | 
 |               appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens)); | 
 |         } | 
 |         timing.end(); | 
 |       } else { | 
 |         timing.begin("NestReduction"); | 
 |         // This pass attempts to reduce the number of nests and nest size | 
 |         // to allow further passes, specifically the class mergers, to do | 
 |         // a better job. This pass is better run before the class merger | 
 |         // but after the publicizer (cannot be run as part of the Enqueuer). | 
 |         new NestReducer(appViewWithLiveness).run(executorService); | 
 |         timing.end(); | 
 |       } | 
 |  | 
 |       boolean isKotlinLibraryCompilationWithInlinePassThrough = | 
 |           options.enableCfByteCodePassThrough && appView.hasCfByteCodePassThroughMethods(); | 
 |  | 
 |       if (!isKotlinLibraryCompilationWithInlinePassThrough | 
 |           && options.getProguardConfiguration().isOptimizing()) { | 
 |         if (options.enableHorizontalClassMerging) { | 
 |           timing.begin("HorizontalStaticClassMerger"); | 
 |           StaticClassMerger staticClassMerger = | 
 |               new StaticClassMerger(appViewWithLiveness, options, mainDexClasses); | 
 |           NestedGraphLense lens = staticClassMerger.run(); | 
 |           if (lens != null) { | 
 |             boolean changed = appView.setGraphLense(lens); | 
 |             assert changed; | 
 |             appViewWithLiveness.setAppInfo( | 
 |                 appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens)); | 
 |           } | 
 |           timing.end(); | 
 |         } | 
 |         if (options.enableVerticalClassMerging) { | 
 |           timing.begin("VerticalClassMerger"); | 
 |           VerticalClassMerger verticalClassMerger = | 
 |               new VerticalClassMerger( | 
 |                   application, appViewWithLiveness, executorService, timing, mainDexClasses); | 
 |           VerticalClassMergerGraphLense lens = verticalClassMerger.run(); | 
 |           if (lens != null) { | 
 |             boolean changed = appView.setGraphLense(lens); | 
 |             assert changed; | 
 |             appView.setVerticallyMergedClasses(verticalClassMerger.getMergedClasses()); | 
 |             application = application.asDirect().rewrittenWithLens(lens); | 
 |             lens.initializeCacheForLookupMethodInAllContexts(); | 
 |             appViewWithLiveness.setAppInfo( | 
 |                 appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens)); | 
 |             lens.unsetCacheForLookupMethodInAllContexts(); | 
 |           } | 
 |           timing.end(); | 
 |         } | 
 |         if (options.enableArgumentRemoval) { | 
 |           SubtypingInfo subtypingInfo = appViewWithLiveness.appInfo().computeSubtypingInfo(); | 
 |           { | 
 |             timing.begin("UnusedArgumentRemoval"); | 
 |             UnusedArgumentsGraphLense lens = | 
 |                 new UnusedArgumentsCollector( | 
 |                         appViewWithLiveness, | 
 |                         new MethodPoolCollection(appViewWithLiveness, subtypingInfo)) | 
 |                     .run(executorService, timing); | 
 |             if (lens != null) { | 
 |               boolean changed = appView.setGraphLense(lens); | 
 |               assert changed; | 
 |               assert application.asDirect().verifyNothingToRewrite(appView, lens); | 
 |               appViewWithLiveness.setAppInfo( | 
 |                   appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens)); | 
 |             } | 
 |             timing.end(); | 
 |           } | 
 |           if (options.enableUninstantiatedTypeOptimization) { | 
 |             timing.begin("UninstantiatedTypeOptimization"); | 
 |             UninstantiatedTypeOptimizationGraphLense lens = | 
 |                 new UninstantiatedTypeOptimization(appViewWithLiveness) | 
 |                     .run( | 
 |                         new MethodPoolCollection(appViewWithLiveness, subtypingInfo), | 
 |                         executorService, | 
 |                         timing); | 
 |             if (lens != null) { | 
 |               boolean changed = appView.setGraphLense(lens); | 
 |               assert changed; | 
 |               assert application.asDirect().verifyNothingToRewrite(appView, lens); | 
 |               appViewWithLiveness.setAppInfo( | 
 |                   appViewWithLiveness.appInfo().rewrittenWithLens(application.asDirect(), lens)); | 
 |             } | 
 |             timing.end(); | 
 |           } | 
 |         } | 
 |       } | 
 |  | 
 |       // None of the optimizations above should lead to the creation of type lattice elements. | 
 |       assert appView.dexItemFactory().verifyNoCachedTypeElements(); | 
 |  | 
 |       // Collect switch maps and ordinals maps. | 
 |       if (options.enableEnumSwitchMapRemoval) { | 
 |         appViewWithLiveness.setAppInfo(new SwitchMapCollector(appViewWithLiveness).run()); | 
 |       } | 
 |       if (options.enableEnumValueOptimization || options.enableEnumUnboxing) { | 
 |         appViewWithLiveness.setAppInfo(new EnumValueInfoMapCollector(appViewWithLiveness).run()); | 
 |       } | 
 |  | 
 |       appView.setAppServices(appView.appServices().rewrittenWithLens(appView.graphLense())); | 
 |  | 
 |       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(); | 
 |       } finally { | 
 |         timing.end(); | 
 |       } | 
 |  | 
 |       // Clear the reference type lattice element cache to reduce memory pressure. | 
 |       appView.dexItemFactory().clearTypeElementsCache(); | 
 |  | 
 |       // At this point all code has been mapped according to the graph lens. We cannot remove the | 
 |       // 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.setGraphLense(new AppliedGraphLens(appView, application.classes())); | 
 |       timing.end(); | 
 |  | 
 |       if (options.printCfg) { | 
 |         if (options.printCfgFile == null || options.printCfgFile.isEmpty()) { | 
 |           System.out.print(printer.toString()); | 
 |         } else { | 
 |           try (OutputStreamWriter writer = new OutputStreamWriter( | 
 |               new FileOutputStream(options.printCfgFile), | 
 |               StandardCharsets.UTF_8)) { | 
 |             writer.write(printer.toString()); | 
 |           } | 
 |         } | 
 |       } | 
 |  | 
 |       // Overwrite SourceFile if specified. This step should be done after IR conversion. | 
 |       timing.begin("Rename SourceFile"); | 
 |       new SourceFileRewriter(appView).run(); | 
 |       timing.end(); | 
 |  | 
 |       // 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; | 
 |         WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null; | 
 |         if (!mainDexRootSet.reasonAsked.isEmpty()) { | 
 |           whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(mainDexKeptGraphConsumer); | 
 |           mainDexKeptGraphConsumer = whyAreYouKeepingConsumer; | 
 |         } | 
 |  | 
 |         Enqueuer enqueuer = | 
 |             EnqueuerFactory.createForMainDexTracing( | 
 |                 appView, | 
 |                 new SubtypingInfo(application.allClasses(), application), | 
 |                 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(); | 
 |         final MainDexClasses finalMainDexClasses = mainDexClasses; | 
 |  | 
 |         processWhyAreYouKeepingAndCheckDiscarded( | 
 |             mainDexRootSet, | 
 |             () -> { | 
 |               ArrayList<DexProgramClass> classes = new ArrayList<>(); | 
 |               // TODO(b/131668850): This is not a deterministic order! | 
 |               finalMainDexClasses | 
 |                   .getClasses() | 
 |                   .forEach( | 
 |                       type -> { | 
 |                         DexClass clazz = appView.definitionFor(type); | 
 |                         assert clazz.isProgramClass(); | 
 |                         classes.add(clazz.asProgramClass()); | 
 |                       }); | 
 |               return classes; | 
 |             }, | 
 |             whyAreYouKeepingConsumer, | 
 |             appView, | 
 |             enqueuer, | 
 |             true, | 
 |             options, | 
 |             timing, | 
 |             executorService); | 
 |       } | 
 |  | 
 |       appView.setAppInfo(new AppInfoWithClassHierarchy(application)); | 
 |  | 
 |       if (options.shouldRerunEnqueuer()) { | 
 |         timing.begin("Post optimization code stripping"); | 
 |         try { | 
 |           GraphConsumer keptGraphConsumer = null; | 
 |           WhyAreYouKeepingConsumer whyAreYouKeepingConsumer = null; | 
 |           if (options.isShrinking()) { | 
 |             keptGraphConsumer = options.keptGraphConsumer; | 
 |             if (!appView.rootSet().reasonAsked.isEmpty()) { | 
 |               whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(keptGraphConsumer); | 
 |               keptGraphConsumer = whyAreYouKeepingConsumer; | 
 |             } | 
 |           } | 
 |  | 
 |           Enqueuer enqueuer = | 
 |               EnqueuerFactory.createForFinalTreeShaking( | 
 |                   appView, | 
 |                   new SubtypingInfo(application.allClasses(), application), | 
 |                   keptGraphConsumer, | 
 |                   missingClasses, | 
 |                   prunedTypes); | 
 |           appView.setAppInfo( | 
 |               enqueuer | 
 |                   .traceApplication( | 
 |                       appView.rootSet(), | 
 |                       options.getProguardConfiguration().getDontWarnPatterns(), | 
 |                       executorService, | 
 |                       timing) | 
 |                   .withEnumValueInfoMaps(enumValueInfoMapCollection)); | 
 |           // Rerunning the enqueuer should not give rise to any method rewritings. | 
 |           assert enqueuer.buildGraphLens(appView) == null; | 
 |           appView.withGeneratedMessageLiteBuilderShrinker( | 
 |               shrinker -> | 
 |                   shrinker.rewriteDeadBuilderReferencesFromDynamicMethods( | 
 |                       appViewWithLiveness, executorService, timing)); | 
 |  | 
 |           if (options.isShrinking()) { | 
 |             // Mark dead proto extensions fields as neither being read nor written. This step must | 
 |             // run prior to the tree pruner. | 
 |             TreePrunerConfiguration treePrunerConfiguration = | 
 |                 appView.withGeneratedExtensionRegistryShrinker( | 
 |                     shrinker -> shrinker.run(enqueuer.getMode()), | 
 |                     DefaultTreePrunerConfiguration.getInstance()); | 
 |  | 
 |             TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration); | 
 |             application = pruner.run(application); | 
 |             Set<DexType> removedClasses = pruner.getRemovedClasses(); | 
 |  | 
 |             if (options.usageInformationConsumer != null) { | 
 |               ExceptionUtils.withFinishedResourceHandler( | 
 |                   options.reporter, options.usageInformationConsumer); | 
 |             } | 
 |             appViewWithLiveness.setAppInfo( | 
 |                 appViewWithLiveness | 
 |                     .appInfo() | 
 |                     .prunedCopyFrom( | 
 |                         application, | 
 |                         CollectionUtils.mergeSets(prunedTypes, removedClasses), | 
 |                         pruner.getMethodsToKeepForConfigurationDebugging())); | 
 |             appView.setAppServices(appView.appServices().prunedCopy(removedClasses)); | 
 |  | 
 |             new BridgeHoisting(appViewWithLiveness).run(); | 
 |  | 
 |             // TODO(b/130721661): Enable this assert. | 
 |             // assert Inliner.verifyNoMethodsInlinedDueToSingleCallSite(appView); | 
 |  | 
 |             assert appView.allMergedClasses().verifyAllSourcesPruned(appViewWithLiveness); | 
 |             assert appView.validateUnboxedEnumsHaveBeenPruned(); | 
 |  | 
 |             processWhyAreYouKeepingAndCheckDiscarded( | 
 |                 appView.rootSet(), | 
 |                 () -> appView.appInfo().app().classesWithDeterministicOrder(), | 
 |                 whyAreYouKeepingConsumer, | 
 |                 appView, | 
 |                 enqueuer, | 
 |                 false, | 
 |                 options, | 
 |                 timing, | 
 |                 executorService); | 
 |  | 
 |             // Remove annotations that refer to types that no longer exist. | 
 |             assert classesToRetainInnerClassAttributeFor != null; | 
 |             AnnotationRemover.builder() | 
 |                 .setClassesToRetainInnerClassAttributeFor(classesToRetainInnerClassAttributeFor) | 
 |                 .build(appView.withLiveness(), removedClasses) | 
 |                 .run(); | 
 |             if (!mainDexClasses.isEmpty()) { | 
 |               // Remove types that no longer exists from the computed main dex list. | 
 |               mainDexClasses = mainDexClasses.prunedCopy(appView.appInfo().withLiveness()); | 
 |             } | 
 |  | 
 |             // Synthesize fields for triggering class initializers. | 
 |             new ClassInitFieldSynthesizer(appViewWithLiveness).run(executorService); | 
 |           } | 
 |         } finally { | 
 |           timing.end(); | 
 |         } | 
 |  | 
 |         if (appView.options().protoShrinking().isProtoShrinkingEnabled()) { | 
 |           IRConverter converter = new IRConverter(appView, timing, null, mainDexClasses); | 
 |  | 
 |           // If proto shrinking is enabled, we need to reprocess every dynamicMethod(). This ensures | 
 |           // that proto fields that have been removed by the second round of tree shaking are also | 
 |           // removed from the proto schemas in the bytecode. | 
 |           appView.withGeneratedMessageLiteShrinker( | 
 |               shrinker -> shrinker.postOptimizeDynamicMethods(converter, executorService, timing)); | 
 |  | 
 |           // If proto shrinking is enabled, we need to post-process every | 
 |           // findLiteExtensionByNumber() method. This ensures that there are no references to dead | 
 |           // extensions that have been removed by the second round of tree shaking. | 
 |           appView.withGeneratedExtensionRegistryShrinker( | 
 |               shrinker -> | 
 |                   shrinker.postOptimizeGeneratedExtensionRegistry( | 
 |                       converter, executorService, timing)); | 
 |         } | 
 |       } | 
 |  | 
 |       // 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()) { | 
 |         SeedMapper seedMapper = | 
 |             SeedMapper.seedMapperFromFile( | 
 |                 options.reporter, options.getProguardConfiguration().getApplyMappingFile()); | 
 |         timing.begin("apply-mapping"); | 
 |         namingLens = | 
 |             new ProguardMapMinifier(appView.withLiveness(), seedMapper) | 
 |                 .run(executorService, timing); | 
 |         timing.end(); | 
 |       } else if (options.isMinifying()) { | 
 |         timing.begin("Minification"); | 
 |         namingLens = new Minifier(appView.withLiveness()).run(executorService, timing); | 
 |         timing.end(); | 
 |       } else { | 
 |         namingLens = NamingLens.getIdentityLens(); | 
 |       } | 
 |  | 
 |       timing.begin("MinifyKotlinMetadata"); | 
 |       new KotlinMetadataRewriter(appView, namingLens).run(executorService); | 
 |       timing.end(); | 
 |  | 
 |       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); | 
 |       timing.end(); | 
 |  | 
 |       // If a method filter is present don't produce output since the application is likely partial. | 
 |       if (options.hasMethodsFilter()) { | 
 |         System.out.println("Finished compilation with method filter: "); | 
 |         options.methodsFilter.forEach(m -> System.out.println("  - " + m)); | 
 |         return; | 
 |       } | 
 |  | 
 |       // Remove unneeded visibility bridges that have been inserted for member rebinding. | 
 |       // This can only be done if we have AppInfoWithLiveness. | 
 |       if (appView.appInfo().hasLiveness()) { | 
 |         new VisibilityBridgeRemover(appView.withLiveness()).run(); | 
 |       } else { | 
 |         // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When | 
 |         // we are not shrinking, we can't move visibility bridges. In principle, though, it would be | 
 |         // possible to remove visibility bridges that have been synthesized by R8, but we currently | 
 |         // do not have this information. | 
 |         assert !options.isShrinking(); | 
 |       } | 
 |  | 
 |       // Validity checks. | 
 |       assert application.classes().stream().allMatch(clazz -> clazz.isValid(options)); | 
 |       if (options.isShrinking() | 
 |           || options.isMinifying() | 
 |           || options.getProguardConfiguration().hasApplyMappingFile()) { | 
 |         assert appView.rootSet().verifyKeptItemsAreKept(application, appView.appInfo()); | 
 |       } | 
 |       assert appView | 
 |           .graphLense() | 
 |           .verifyMappingToOriginalProgram( | 
 |               application.classesWithDeterministicOrder(), | 
 |               new ApplicationReader(inputApp.withoutMainDexList(), options, timing) | 
 |                   .read(executorService), | 
 |               appView.dexItemFactory()); | 
 |  | 
 |       // Report synthetic rules (only for testing). | 
 |       // TODO(b/120959039): Move this to being reported through the graph consumer. | 
 |       if (options.syntheticProguardRulesConsumer != null) { | 
 |         options.syntheticProguardRulesConsumer.accept(synthesizedProguardRules); | 
 |       } | 
 |  | 
 |       NamingLens prefixRewritingNamingLens = | 
 |           PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView, namingLens); | 
 |  | 
 |       new GenericSignatureRewriter(appView, prefixRewritingNamingLens) | 
 |           .run(appView.appInfo().classes(), executorService); | 
 |  | 
 |       // Generate the resulting application resources. | 
 |       writeApplication( | 
 |           executorService, | 
 |           application, | 
 |           appView, | 
 |           appView.graphLense(), | 
 |           appView.initClassLens(), | 
 |           prefixRewritingNamingLens, | 
 |           options, | 
 |           ProguardMapSupplier.create(classNameMapper, options)); | 
 |  | 
 |       options.printWarnings(); | 
 |     } catch (ExecutionException e) { | 
 |       throw unwrapExecutionException(e); | 
 |     } finally { | 
 |       options.signalFinishedToConsumers(); | 
 |       // Dump timings. | 
 |       if (options.printTimes) { | 
 |         timing.report(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   private AppView<AppInfoWithLiveness> runEnqueuer( | 
 |       AnnotationRemover.Builder annotationRemoverBuilder, | 
 |       ExecutorService executorService, | 
 |       AppView<AppInfoWithClassHierarchy> appView, | 
 |       SubtypingInfo subtypingInfo) | 
 |       throws ExecutionException { | 
 |     Enqueuer enqueuer = EnqueuerFactory.createForInitialTreeShaking(appView, subtypingInfo); | 
 |     enqueuer.setAnnotationRemoverBuilder(annotationRemoverBuilder); | 
 |     if (appView.options().enableInitializedClassesInInstanceMethodsAnalysis) { | 
 |       enqueuer.registerAnalysis(new InitializedClassesInInstanceMethodsAnalysis(appView)); | 
 |     } | 
 |     if (AssertionsRewriter.isEnabled(appView.options())) { | 
 |       enqueuer.registerAnalysis( | 
 |           new ClassInitializerAssertionEnablingAnalysis( | 
 |               appView.dexItemFactory(), OptimizationFeedbackSimple.getInstance())); | 
 |     } | 
 |  | 
 |     AppView<AppInfoWithLiveness> appViewWithLiveness = | 
 |         appView.setAppInfo( | 
 |             enqueuer.traceApplication( | 
 |                 appView.rootSet(), | 
 |                 options.getProguardConfiguration().getDontWarnPatterns(), | 
 |                 executorService, | 
 |                 timing)); | 
 |     NestedGraphLense lens = enqueuer.buildGraphLens(appView); | 
 |     if (lens != null) { | 
 |       appView.setGraphLense(lens); | 
 |       appViewWithLiveness.setAppInfo( | 
 |           appViewWithLiveness | 
 |               .appInfo() | 
 |               .rewrittenWithLens(appView.appInfo().app().asDirect(), lens)); | 
 |     } | 
 |     if (InternalOptions.assertionsEnabled()) { | 
 |       // Register the dead proto types. These are needed to verify that no new missing types are | 
 |       // reported and that no dead proto types are referenced in the generated application. | 
 |       appViewWithLiveness.withProtoShrinker( | 
 |           shrinker -> | 
 |               shrinker.setDeadProtoTypes(appViewWithLiveness.appInfo().getDeadProtoTypes())); | 
 |     } | 
 |     appView.withGeneratedMessageLiteBuilderShrinker( | 
 |         shrinker -> | 
 |             shrinker.rewriteDeadBuilderReferencesFromDynamicMethods( | 
 |                 appViewWithLiveness, executorService, timing)); | 
 |     return appViewWithLiveness; | 
 |   } | 
 |  | 
 |   static void processWhyAreYouKeepingAndCheckDiscarded( | 
 |       RootSet rootSet, | 
 |       Supplier<Iterable<DexProgramClass>> classes, | 
 |       WhyAreYouKeepingConsumer whyAreYouKeepingConsumer, | 
 |       AppView<? extends AppInfoWithClassHierarchy> appView, | 
 |       Enqueuer enqueuer, | 
 |       boolean forMainDex, | 
 |       InternalOptions options, | 
 |       Timing timing, | 
 |       ExecutorService executorService) | 
 |       throws ExecutionException { | 
 |     if (whyAreYouKeepingConsumer != null) { | 
 |       for (DexReference reference : rootSet.reasonAsked) { | 
 |         whyAreYouKeepingConsumer.printWhyAreYouKeeping( | 
 |             enqueuer.getGraphReporter().getGraphNode(reference), System.out); | 
 |       } | 
 |     } | 
 |     if (rootSet.checkDiscarded.isEmpty() | 
 |         || appView.options().testing.dontReportFailingCheckDiscarded) { | 
 |       return; | 
 |     } | 
 |     List<DexDefinition> failed = new DiscardedChecker(rootSet, classes.get()).run(); | 
 |     if (failed.isEmpty()) { | 
 |       return; | 
 |     } | 
 |     // If there is no kept-graph info, re-run the enqueueing to compute it. | 
 |     if (whyAreYouKeepingConsumer == null) { | 
 |       whyAreYouKeepingConsumer = new WhyAreYouKeepingConsumer(null); | 
 |       SubtypingInfo subtypingInfo = | 
 |           new SubtypingInfo(appView.appInfo().app().asDirect().allClasses(), appView); | 
 |       if (forMainDex) { | 
 |         enqueuer = | 
 |             EnqueuerFactory.createForMainDexTracing( | 
 |                 appView, subtypingInfo, whyAreYouKeepingConsumer); | 
 |         enqueuer.traceMainDex(rootSet, executorService, timing); | 
 |       } else { | 
 |         enqueuer = | 
 |             EnqueuerFactory.createForWhyAreYouKeeping( | 
 |                 appView, subtypingInfo, whyAreYouKeepingConsumer); | 
 |         enqueuer.traceApplication( | 
 |             rootSet, | 
 |             options.getProguardConfiguration().getDontWarnPatterns(), | 
 |             executorService, | 
 |             timing); | 
 |       } | 
 |     } | 
 |     for (DexDefinition definition : failed) { | 
 |       ByteArrayOutputStream baos = new ByteArrayOutputStream(); | 
 |       whyAreYouKeepingConsumer.printWhyAreYouKeeping( | 
 |           enqueuer.getGraphReporter().getGraphNode(definition.toReference()), | 
 |           new PrintStream(baos)); | 
 |       options.reporter.info( | 
 |           new StringDiagnostic( | 
 |               "Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString())); | 
 |     } | 
 |     if (!options.testing.allowCheckDiscardedErrors) { | 
 |       throw new CompilationError("Discard checks failed."); | 
 |     } | 
 |   } | 
 |  | 
 |   private static boolean verifyNoJarApplicationReaders(List<DexProgramClass> classes) { | 
 |     for (DexProgramClass clazz : classes) { | 
 |       for (DexEncodedMethod method : clazz.methods()) { | 
 |         if (method.getCode() != null) { | 
 |           assert method.getCode().verifyNoInputReaders(); | 
 |         } | 
 |       } | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   private static void run(String[] args) throws CompilationFailedException { | 
 |     R8Command command = R8Command.parse(args, CommandLineOrigin.INSTANCE).build(); | 
 |     if (command.isPrintHelp()) { | 
 |       SelfRetraceTest.test(); | 
 |       System.out.println(USAGE_MESSAGE); | 
 |       return; | 
 |     } | 
 |     if (command.isPrintVersion()) { | 
 |       System.out.println("R8 " + Version.getVersionString()); | 
 |       return; | 
 |     } | 
 |     InternalOptions options = command.getInternalOptions(); | 
 |     ExecutorService executorService = ThreadUtils.getExecutorService(options); | 
 |     try { | 
 |       ExceptionUtils.withR8CompilationHandler(options.reporter, () -> | 
 |           run(command.getInputApp(), options, executorService)); | 
 |     } finally { | 
 |       executorService.shutdown(); | 
 |     } | 
 |   } | 
 |  | 
 |   /** | 
 |    * Command-line entry to R8. | 
 |    * | 
 |    * See {@link R8Command#USAGE_MESSAGE} or run {@code r8 --help} for usage information. | 
 |    */ | 
 |   public static void main(String[] args) { | 
 |     if (args.length == 0) { | 
 |       System.err.println(USAGE_MESSAGE); | 
 |       System.exit(ExceptionUtils.STATUS_ERROR); | 
 |     } | 
 |     ExceptionUtils.withMainProgramHandler(() -> run(args)); | 
 |   } | 
 | } |