| // Copyright (c) 2024, 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 com.android.tools.r8.dex.ApplicationReader; | |
| import com.android.tools.r8.diagnostic.R8VersionDiagnostic; | |
| import com.android.tools.r8.features.FeatureSplitConfiguration; | |
| import com.android.tools.r8.graph.DexClasspathClass; | |
| import com.android.tools.r8.graph.DirectMappedDexApplication; | |
| import com.android.tools.r8.keepanno.ast.KeepDeclaration; | |
| import com.android.tools.r8.metadata.impl.R8PartialCompilationStatsMetadataBuilder; | |
| import com.android.tools.r8.partial.R8PartialD8Input; | |
| import com.android.tools.r8.partial.R8PartialD8Result; | |
| import com.android.tools.r8.partial.R8PartialProgramPartitioning; | |
| import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialD8SubCompilationConfiguration; | |
| import com.android.tools.r8.partial.R8PartialSubCompilationConfiguration.R8PartialR8SubCompilationConfiguration; | |
| import com.android.tools.r8.profile.art.ArtProfileOptions; | |
| import com.android.tools.r8.profile.startup.StartupOptions; | |
| import com.android.tools.r8.utils.AndroidApp; | |
| import com.android.tools.r8.utils.ExceptionUtils; | |
| import com.android.tools.r8.utils.FeatureSplitConsumers; | |
| import com.android.tools.r8.utils.ForwardingDiagnosticsHandler; | |
| import com.android.tools.r8.utils.InternalClasspathOrLibraryClassProvider; | |
| import com.android.tools.r8.utils.InternalOptions; | |
| import com.android.tools.r8.utils.InternalProgramClassProvider; | |
| import com.android.tools.r8.utils.ThreadUtils; | |
| import com.android.tools.r8.utils.internal.ListUtils; | |
| import com.android.tools.r8.utils.internal.exceptions.Unreachable; | |
| import com.android.tools.r8.utils.timing.Timing; | |
| import java.io.IOException; | |
| import java.util.Collection; | |
| import java.util.Collections; | |
| import java.util.IdentityHashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.concurrent.ExecutorService; | |
| import java.util.function.Consumer; | |
| class R8Partial { | |
| private final InternalOptions options; | |
| private final Timing timing; | |
| R8Partial(InternalOptions options, ExecutorService executor) { | |
| this.options = options; | |
| this.timing = Timing.createRoot("R8 partial " + Version.LABEL, options, executor); | |
| } | |
| static void runForTesting(AndroidApp app, InternalOptions options) | |
| throws CompilationFailedException { | |
| ExecutorService executor = ThreadUtils.getExecutorService(options); | |
| ExceptionUtils.withR8CompilationHandler( | |
| options.reporter, | |
| () -> { | |
| try { | |
| new R8Partial(options, executor).runInternal(app, executor); | |
| } finally { | |
| executor.shutdown(); | |
| } | |
| }); | |
| } | |
| void runInternal(AndroidApp app, ExecutorService executor) throws IOException, ResourceException { | |
| if (options.getProguardConfiguration().isProtoShrinkingEnabled()) { | |
| throw options.reporter.fatalError("Partial shrinking does not support proto shrinking"); | |
| } | |
| Map<FeatureSplit, FeatureSplitConsumers> featureSplitConsumers = | |
| getAndClearFeatureSplitConsumers(); | |
| timing.begin("Process input"); | |
| R8PartialD8Input input = runProcessInputStep(app, executor); | |
| R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder = | |
| R8PartialCompilationStatsMetadataBuilder.create(input, options); | |
| timing.end().begin("Run D8"); | |
| R8PartialD8Result d8Result = runD8Step(input, executor); | |
| timing.end(); | |
| setFeatureSplitConsumers(featureSplitConsumers); | |
| lockFeatureSplitProgramResourceProviders(); | |
| timing.begin("Run R8"); | |
| runR8Step(app, d8Result, statsMetadataBuilder, executor); | |
| timing.end(); | |
| if (options.isPerfettoTracingEnabled()) { | |
| timing.report(); | |
| } | |
| } | |
| private R8PartialD8Input runProcessInputStep(AndroidApp androidApp, ExecutorService executor) | |
| throws IOException { | |
| DirectMappedDexApplication app = | |
| new ApplicationReader(androidApp, options, timing).readDirect(executor); | |
| List<KeepDeclaration> keepDeclarations = app.getKeepDeclarations(); | |
| R8PartialProgramPartitioning partitioning = R8PartialProgramPartitioning.create(app); | |
| partitioning.printForTesting(options); | |
| options.getLibraryDesugaringOptions().loadMachineDesugaredLibrarySpecification(timing, app); | |
| return new R8PartialD8Input( | |
| partitioning.getD8Classes(), | |
| partitioning.getR8Classes(), | |
| app.classpathClasses(), | |
| app.libraryClasses(), | |
| app.getFlags(), | |
| keepDeclarations); | |
| } | |
| private R8PartialD8Result runD8Step(R8PartialD8Input input, ExecutorService executor) | |
| throws IOException { | |
| // TODO(b/389039057): This will desugar the entire R8 part. For build speed, look into if some | |
| // desugarings can be postponed to the R8 compilation, since we do not desugar dead code in R8. | |
| // As a simple example, it should be safe to postpone backporting to the R8 compilation. | |
| D8Command.Builder d8Builder = | |
| D8Command.builder(options.reporter) | |
| .setEnableExperimentalMissingLibraryApiModeling( | |
| options.apiModelingOptions().isApiModelingEnabled()) | |
| .setEnableVerboseSyntheticNames( | |
| options.desugarSpecificOptions().enableVerboseSyntheticNames) | |
| .setMinApiLevel(options.getMinApiLevel().asUnchecked()) | |
| .setMode(options.getCompilationMode()) | |
| .setProgramConsumer(DexIndexedConsumer.emptyConsumer()); | |
| input.configure(d8Builder); | |
| d8Builder.getAppBuilder().disableAndroidJarHiddenClassExtension(); | |
| d8Builder.validate(); | |
| D8Command d8Command = d8Builder.makeD8Command(options.dexItemFactory()); | |
| AndroidApp d8App = d8Command.getInputApp(); | |
| InternalOptions d8Options = d8Command.getInternalOptions(); | |
| forwardOptions(d8Options); | |
| options.partialCompilationConfiguration.d8DexOptionsConsumer.accept(d8Options); | |
| R8PartialD8SubCompilationConfiguration subCompilationConfiguration = | |
| new R8PartialD8SubCompilationConfiguration( | |
| input.getD8Types(), | |
| input.getR8Types(), | |
| input.getFlags(), | |
| options.getLibraryDesugaringOptions(), | |
| timing); | |
| d8Options.setArtProfileOptions( | |
| new ArtProfileOptions(d8Options, options.getArtProfileOptions())); | |
| d8Options.setFeatureSplitConfiguration(options.getFeatureSplitConfiguration()); | |
| d8Options.setStartupOptions(new StartupOptions(d8Options, options.getStartupOptions())); | |
| d8Options.partialSubCompilationConfiguration = subCompilationConfiguration; | |
| D8.runInternal(d8App, d8Options, executor); | |
| return new R8PartialD8Result( | |
| subCompilationConfiguration.getArtProfiles(), | |
| subCompilationConfiguration.getClassToFeatureSplitMap(), | |
| subCompilationConfiguration.getDexedOutputClasses(), | |
| subCompilationConfiguration.getDesugaredOutputClasses(), | |
| input.getFlags(), | |
| input.getKeepDeclarations(), | |
| subCompilationConfiguration.getOutputClasspathClasses(), | |
| subCompilationConfiguration.getOutputLibraryClasses(), | |
| subCompilationConfiguration.getStartupProfile(), | |
| subCompilationConfiguration.getSynthetics()); | |
| } | |
| private void runR8Step( | |
| AndroidApp app, | |
| R8PartialD8Result d8Result, | |
| R8PartialCompilationStatsMetadataBuilder statsMetadataBuilder, | |
| ExecutorService executor) | |
| throws IOException { | |
| // Compile R8 input with R8 using the keep rules from trace references. | |
| DiagnosticsHandler r8DiagnosticsHandler = | |
| new ForwardingDiagnosticsHandler(options.reporter) { | |
| @Override | |
| public DiagnosticsLevel modifyDiagnosticsLevel( | |
| DiagnosticsLevel level, Diagnostic diagnostic) { | |
| if (diagnostic instanceof R8VersionDiagnostic) { | |
| return DiagnosticsLevel.NONE; | |
| } | |
| return super.modifyDiagnosticsLevel(level, diagnostic); | |
| } | |
| }; | |
| // TODO(b/390389764): Disable desugaring. | |
| R8Command.Builder r8Builder = | |
| R8Command.builder(r8DiagnosticsHandler) | |
| .addProgramResourceProvider( | |
| new InternalProgramClassProvider(d8Result.getDesugaredClasses())) | |
| .addClasspathResourceProvider( | |
| new InternalClasspathOrLibraryClassProvider<>( | |
| DexClasspathClass.toClasspathClasses(d8Result.getDexedClasses()))) | |
| .addClasspathResourceProvider( | |
| new InternalClasspathOrLibraryClassProvider<>(d8Result.getOutputClasspathClasses())) | |
| .addLibraryResourceProvider( | |
| new InternalClasspathOrLibraryClassProvider<>(d8Result.getOutputLibraryClasses())) | |
| .enableLegacyFullModeForKeepRules(true) | |
| .setBuildMetadataConsumer(options.r8BuildMetadataConsumer) | |
| .setEnableExperimentalMissingLibraryApiModeling( | |
| options.apiModelingOptions().isApiModelingEnabled()) | |
| .setEnableVerboseSyntheticNames( | |
| options.desugarSpecificOptions().enableVerboseSyntheticNames) | |
| .setMapIdProvider(options.mapIdProvider) | |
| .setMinApiLevel(options.getMinApiLevel().asUnchecked()) | |
| .setMode(options.getCompilationMode()) | |
| .setPartialCompilationConfiguration(options.partialCompilationConfiguration) | |
| .setProgramConsumer(options.programConsumer) | |
| .setSourceFileProvider(options.sourceFileProvider) | |
| .setNativeReferencesConsumer(options.nativeReferencesConsumer); | |
| // The program input that R8 must compile is provided above using an | |
| // InternalProgramClassProvider. This passes in the data resources that we must either rewrite | |
| // or pass through. | |
| for (ProgramResourceProvider programResourceProvider : app.getProgramResourceProviders()) { | |
| if (programResourceProvider.getDataResourceProvider() == null) { | |
| programResourceProvider.finished(options.reporter); | |
| } else { | |
| r8Builder.addProgramResourceProvider( | |
| new ProgramResourceProvider() { | |
| @Override | |
| public Collection<ProgramResource> getProgramResources() { | |
| return Collections.emptyList(); | |
| } | |
| @Override | |
| public void getProgramResources(Consumer<ProgramResource> consumer) { | |
| // Intentionally empty. | |
| } | |
| @Override | |
| public DataResourceProvider getDataResourceProvider() { | |
| return programResourceProvider.getDataResourceProvider(); | |
| } | |
| @Override | |
| public void finished(DiagnosticsHandler handler) throws IOException { | |
| programResourceProvider.finished(handler); | |
| } | |
| }); | |
| } | |
| } | |
| r8Builder.getAppBuilder().disableAndroidJarHiddenClassExtension(); | |
| r8Builder.validate(); | |
| R8Command r8Command = | |
| r8Builder.makeR8Command(options.dexItemFactory(), options.getProguardConfiguration()); | |
| AndroidApp r8App = r8Command.getInputApp(); | |
| InternalOptions r8Options = r8Command.getInternalOptions(); | |
| forwardOptions(r8Options); | |
| options.partialCompilationConfiguration.r8OptionsConsumer.accept(r8Options); | |
| r8Options.partialSubCompilationConfiguration = | |
| new R8PartialR8SubCompilationConfiguration( | |
| d8Result.getArtProfiles(), | |
| d8Result.getClassToFeatureSplitMap(), | |
| d8Result.getDexedClasses(), | |
| d8Result.getFlags(), | |
| d8Result.getKeepDeclarations(), | |
| d8Result.getStartupProfile(), | |
| statsMetadataBuilder, | |
| d8Result.getSynthetics(), | |
| timing); | |
| r8Options.setArtProfileOptions( | |
| new ArtProfileOptions(r8Options, options.getArtProfileOptions())); | |
| r8Options.setFeatureSplitConfiguration(options.getFeatureSplitConfiguration()); | |
| r8Options.setStartupOptions(new StartupOptions(r8Options, options.getStartupOptions())); | |
| r8Options.setKeepSpecificationSources(options.getKeepSpecifications()); | |
| r8Options.getTestingOptions().enableEmbeddedKeepAnnotations = | |
| options.getTestingOptions().enableEmbeddedKeepAnnotations; | |
| r8Options.configurationConsumer = options.configurationConsumer; | |
| r8Options.mapConsumer = options.mapConsumer; | |
| r8Options.proguardSeedsConsumer = options.proguardSeedsConsumer; | |
| r8Options.usageInformationConsumer = options.usageInformationConsumer; | |
| r8Options.sourceFileProvider = options.sourceFileProvider; | |
| r8Options | |
| .getLibraryDesugaringOptions() | |
| .setMachineDesugaredLibrarySpecification( | |
| options.getLibraryDesugaringOptions().getMachineDesugaredLibrarySpecification()) | |
| .setTypeRewriter(options.getLibraryDesugaringOptions().getTypeRewriter()); | |
| if (options.androidResourceProvider != null) { | |
| r8Options.androidResourceProvider = options.androidResourceProvider; | |
| r8Options.androidResourceConsumer = options.androidResourceConsumer; | |
| r8Options.resourceShrinkerConfiguration = options.resourceShrinkerConfiguration; | |
| } | |
| R8.runInternal(r8App, r8Options, executor); | |
| } | |
| private Map<FeatureSplit, FeatureSplitConsumers> getAndClearFeatureSplitConsumers() { | |
| FeatureSplitConfiguration featureSplitConfiguration = options.getFeatureSplitConfiguration(); | |
| if (featureSplitConfiguration == null) { | |
| return null; | |
| } | |
| Map<FeatureSplit, FeatureSplitConsumers> featureSplitConsumers = new IdentityHashMap<>(); | |
| for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) { | |
| featureSplitConsumers.put(featureSplit, featureSplit.internalClearConsumers()); | |
| } | |
| return featureSplitConsumers; | |
| } | |
| private void setFeatureSplitConsumers( | |
| Map<FeatureSplit, FeatureSplitConsumers> featureSplitConsumers) { | |
| if (featureSplitConsumers == null) { | |
| return; | |
| } | |
| FeatureSplitConfiguration featureSplitConfiguration = options.getFeatureSplitConfiguration(); | |
| for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) { | |
| featureSplit.internalSetConsumers(featureSplitConsumers.get(featureSplit)); | |
| } | |
| featureSplitConsumers.clear(); | |
| } | |
| private void lockFeatureSplitProgramResourceProviders() { | |
| FeatureSplitConfiguration featureSplitConfiguration = options.getFeatureSplitConfiguration(); | |
| if (featureSplitConfiguration == null) { | |
| return; | |
| } | |
| for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) { | |
| List<ProgramResourceProvider> programResourceProviders = | |
| featureSplit.getProgramResourceProviders(); | |
| List<ProgramResourceProvider> replacementProgramResourceProviders = | |
| ListUtils.map( | |
| programResourceProviders, | |
| programResourceProvider -> | |
| new ProgramResourceProvider() { | |
| @Override | |
| public Collection<ProgramResource> getProgramResources() { | |
| throw new Unreachable(); | |
| } | |
| @Override | |
| public void getProgramResources(Consumer<ProgramResource> consumer) { | |
| throw new Unreachable(); | |
| } | |
| @Override | |
| public DataResourceProvider getDataResourceProvider() { | |
| return programResourceProvider.getDataResourceProvider(); | |
| } | |
| @Override | |
| public void finished(DiagnosticsHandler handler) throws IOException { | |
| programResourceProvider.finished(handler); | |
| } | |
| }); | |
| featureSplit.internalSetProgramResourceProviders(replacementProgramResourceProviders); | |
| } | |
| } | |
| private void forwardOptions(InternalOptions subCompilationOptions) { | |
| subCompilationOptions.emitNestAnnotationsInDex = options.emitNestAnnotationsInDex; | |
| subCompilationOptions.emitRecordAnnotationsInDex = options.emitRecordAnnotationsInDex; | |
| subCompilationOptions.emitPermittedSubclassesAnnotationsInDex = | |
| options.emitPermittedSubclassesAnnotationsInDex; | |
| subCompilationOptions.desugarState = options.desugarState; | |
| subCompilationOptions.forceNestDesugaring = options.forceNestDesugaring; | |
| subCompilationOptions.emitLambdaMethodAnnotations = options.emitLambdaMethodAnnotations; | |
| subCompilationOptions.disableLambdaMethodAnnotations = options.disableLambdaMethodAnnotations; | |
| subCompilationOptions.getKeepRadiusOptions().keepRadiusContainerConsumer = | |
| options.getKeepRadiusOptions().keepRadiusContainerConsumer; | |
| subCompilationOptions.getTestingOptions().forceDexContainerFormat = | |
| options.getTestingOptions().forceDexContainerFormat; | |
| subCompilationOptions.getTestingOptions().enableDexConstString20 = | |
| options.getTestingOptions().enableDexConstString20; | |
| subCompilationOptions.getTestingOptions().enableDexVersion35UpToApi30 = | |
| options.getTestingOptions().enableDexVersion35UpToApi30; | |
| } | |
| } |