blob: edc8290765d08ec2f58099331203db29fc44dd01 [file]
// 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.jar;
import static com.android.tools.r8.utils.positions.LineNumberOptimizer.runAndWriteMap;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumerUtils;
import com.android.tools.r8.SourceFileEnvironment;
import com.android.tools.r8.debuginfo.DebugRepresentation;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapSupplierResult;
import com.android.tools.r8.threading.TaskCollection;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalGlobalSyntheticsProgramConsumer.InternalGlobalSyntheticsCfConsumer;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OriginalSourceFiles;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.timing.Timing;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
public class CfApplicationWriter {
private final DexApplication application;
private final AppView<?> appView;
private final InternalOptions options;
private final Optional<Marker> marker;
public CfApplicationWriter(AppView<?> appView, Marker marker) {
this.application = appView.appInfo().app();
this.appView = appView;
this.options = appView.options();
this.marker = Optional.ofNullable(marker);
}
public void write(ClassFileConsumer consumer, ExecutorService executorService)
throws ExecutionException {
assert !options.hasMappingFileSupport();
write(consumer, executorService, null);
}
public void write(
ClassFileConsumer consumer, ExecutorService executorService, AndroidApp inputApp)
throws ExecutionException {
application.timing.begin("CfApplicationWriter.write");
try {
writeApplication(inputApp, consumer, executorService);
} finally {
application.timing.end();
}
}
private boolean includeMarker(Marker marker) {
if (marker.isRelocator()) {
return false;
}
assert marker.isCfBackend() || marker.isDexBackend();
if (options.desugarSpecificOptions().noCfMarkerForDesugaredCode) {
return !marker.isCfBackend() || !marker.isDesugared();
}
return true;
}
private void writeApplication(
AndroidApp inputApp, ClassFileConsumer consumer, ExecutorService executorService)
throws ExecutionException {
Timing timing = appView.appInfo().app().timing;
timing.begin("CfApplication.write");
ProguardMapSupplierResult mapSupplierResult = ProguardMapSupplierResult.createEmpty();
try {
if (options.hasMappingFileSupport()) {
assert marker.isPresent();
mapSupplierResult =
runAndWriteMap(
inputApp,
appView,
executorService,
timing,
OriginalSourceFiles.fromClasses(),
DebugRepresentation.none(options));
marker.get().setPgMapId(mapSupplierResult.awaitProguardMapId().getId());
}
Optional<String> markerString = marker.filter(this::includeMarker).map(Marker::toString);
SourceFileEnvironment sourceFileEnvironment = null;
if (options.sourceFileProvider != null) {
sourceFileEnvironment =
ApplicationWriter.createSourceFileEnvironment(mapSupplierResult.awaitProguardMapId());
}
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView);
Collection<DexProgramClass> classes = application.classes();
Collection<DexProgramClass> globalSyntheticClasses = new ArrayList<>();
if (options.intermediate && options.hasGlobalSyntheticsConsumer()) {
Collection<DexProgramClass> allClasses = classes;
classes = new ArrayList<>(allClasses.size());
for (DexProgramClass clazz : allClasses) {
if (appView.getSyntheticItems().isGlobalSyntheticClassTransitive(clazz)) {
globalSyntheticClasses.add(clazz);
Consumer<DexProgramClass> globalSyntheticCreatedCallback =
appView.options().testing.globalSyntheticCreatedCallback;
if (globalSyntheticCreatedCallback != null) {
globalSyntheticCreatedCallback.accept(clazz);
}
} else {
classes.add(clazz);
}
}
}
supplyConsumer(
consumer,
classes,
markerString,
rewriter,
sourceFileEnvironment,
executorService,
timing);
if (!globalSyntheticClasses.isEmpty()) {
InternalGlobalSyntheticsCfConsumer globalsConsumer =
new InternalGlobalSyntheticsCfConsumer(options.getGlobalSyntheticsConsumer(), appView);
supplyConsumer(
globalsConsumer,
globalSyntheticClasses,
markerString,
rewriter,
sourceFileEnvironment,
executorService,
timing);
globalsConsumer.finished(appView);
}
ApplicationWriter.supplyAdditionalConsumers(appView, Collections.emptyList());
} finally {
mapSupplierResult.await();
timing.end();
}
}
private void supplyConsumer(
ClassFileConsumer consumer,
Collection<DexProgramClass> classes,
Optional<String> markerString,
LensCodeRewriterUtils rewriter,
SourceFileEnvironment sourceFileEnvironment,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
TaskCollection<CfApplicationClassWriter.Result> taskCollection =
new TaskCollection<>(options.getThreadingModule(), executorService, classes.size());
taskCollection.stream(
sortClassesForWriting(consumer, classes),
(clazz, threadTiming) ->
new CfApplicationClassWriter(appView, clazz)
.writeClassCatchingErrors(rewriter, markerString, sourceFileEnvironment),
result -> supplyConsumer(consumer, result),
options,
timing,
clazz -> "Compute bytes for " + clazz.getTypeName());
}
private Collection<DexProgramClass> sortClassesForWriting(
ClassFileConsumer consumer, Collection<DexProgramClass> classes) {
if (consumer instanceof ClassFileConsumer.ArchiveConsumer) {
// When compiling to an archive, sort the classes by their obfuscated name to allow writing
// the archive on-the-fly meanwhile preserving determinism of the output archive.
List<Pair<String, DexProgramClass>> entries =
ListUtils.map(
classes,
clazz ->
new Pair<>(
appView.getNamingLens().lookupClassDescriptor(clazz.getType()).toString(),
clazz));
// Sort by descriptor (this is equivalent to sorting by .class file entry name).
ListUtils.destructiveSort(entries, Comparator.comparing(Pair::getFirst));
return ListUtils.map(entries, Pair::getSecond);
} else {
return classes;
}
}
private void supplyConsumer(ClassFileConsumer consumer, CfApplicationClassWriter.Result result) {
ExceptionUtils.withConsumeResourceHandler(
options.reporter,
handler -> {
if (consumer instanceof ClassFileConsumer.ArchiveConsumer) {
ClassFileConsumer.ArchiveConsumer archiveConsumer =
(ClassFileConsumer.ArchiveConsumer) consumer;
ClassFileConsumerUtils.ArchiveConsumerUtils.writeFileNow(
archiveConsumer, result.getClassFileData(), result.getDescriptor(), handler);
result.clear();
} else {
consumer.accept(
ByteDataView.of(result.getClassFileData()), result.getDescriptor(), handler);
}
});
}
}