blob: 5a4298c7c5b52a31e8b5daa3cfb1789bb0777291 [file] [log] [blame]
// 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;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DexIndexedConsumerUtils;
import com.android.tools.r8.utils.DexUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.StringDiagnostic;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Set;
/**
* Consumer for DEX encoded programs.
*
* <p>This consumer receives DEX file content using standard indexed-multidex for programs larger
* than a single DEX file. This is the default consumer for DEX programs.
*/
@KeepForApi
public interface DexIndexedConsumer extends ProgramConsumer, ByteBufferProvider {
/**
* Callback to receive DEX data for a compilation output.
*
* <p>This is the equivalent to writing out the files classes.dex, classes2.dex, etc., where
* fileIndex gives the current file count (with the first file having index zero).
*
* <p>There is no guaranteed order and files might be written concurrently.
*
* <p>The consumer is expected not to throw, but instead report any errors via the diagnostics
* {@param handler}. If an error is reported via {@param handler} and no exceptions are thrown,
* then the compiler guaranties to exit with an error.
*
* <p>The {@link ByteDataView} {@param data} object can only be assumed valid during the duration
* of the accept. If the bytes are needed beyond that, a copy must be made elsewhere.
*
* @param fileIndex Index of the DEX file for multi-dexing. Files are zero-indexed.
* @param data DEX encoded data in a ByteDataView wrapper.
* @param descriptors Class descriptors for all classes defined in the DEX data.
* @param handler Diagnostics handler for reporting.
*/
default void accept(
int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
// To avoid breaking binary compatiblity, old consumers not implementing the new API will be
// forwarded to. New consumers must implement the accept on ByteDataView.
accept(fileIndex, data.copyByteData(), descriptors, handler);
}
// Any new implementation should not use or call the deprecated accept method.
@Deprecated
default void accept(
int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
handler.error(
new StringDiagnostic("Deprecated use of DexIndexedConsumer::accept(..., byte[], ...)"));
}
/** Empty consumer to request the production of the resource but ignore its value. */
static DexIndexedConsumer emptyConsumer() {
return ForwardingConsumer.EMPTY_CONSUMER;
}
/** Forwarding consumer to delegate to an optional existing consumer. */
@KeepForApi
class ForwardingConsumer implements DexIndexedConsumer {
private static final DexIndexedConsumer EMPTY_CONSUMER = new ForwardingConsumer(null);
private final DexIndexedConsumer consumer;
public ForwardingConsumer(DexIndexedConsumer consumer) {
this.consumer = consumer;
}
@Override
public DataResourceConsumer getDataResourceConsumer() {
return consumer != null ? consumer.getDataResourceConsumer() : null;
}
@Override
public void accept(
int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
if (consumer != null) {
consumer.accept(fileIndex, data, descriptors, handler);
}
}
@Override
public void finished(DiagnosticsHandler handler) {
if (consumer != null) {
consumer.finished(handler);
}
}
}
/** Consumer to write program resources to an output. */
@KeepForApi
class ArchiveConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
protected final OutputBuilder outputBuilder;
protected final boolean consumeDataResources;
public ArchiveConsumer(Path archive) {
this(archive, null, false);
}
public ArchiveConsumer(Path archive, boolean consumeDataResources) {
this(archive, null, consumeDataResources);
}
public ArchiveConsumer(Path archive, DexIndexedConsumer consumer) {
this(archive, consumer, false);
}
public ArchiveConsumer(
Path archive, DexIndexedConsumer consumer, boolean consumeDataResources) {
super(consumer);
this.outputBuilder = new ArchiveBuilder(archive);
this.consumeDataResources = consumeDataResources;
this.outputBuilder.open();
if (getDataResourceConsumer() != null) {
this.outputBuilder.open();
}
}
public Origin getOrigin() {
return outputBuilder.getOrigin();
}
@Override
public DataResourceConsumer getDataResourceConsumer() {
return consumeDataResources ? this : null;
}
@Override
public void accept(
int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
outputBuilder.addIndexedClassFile(
fileIndex, DexUtils.getDefaultDexFileName(fileIndex), data, handler);
}
@Override
public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
outputBuilder.addDirectory(directory.getName(), handler);
}
@Override
public void accept(DataEntryResource file, DiagnosticsHandler handler) {
outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
outputBuilder.close(handler);
}
@Override
public Path internalGetOutputPath() {
return outputBuilder.getPath();
}
}
@KeepForApi
class DirectoryConsumer extends ForwardingConsumer
implements DataResourceConsumer, InternalProgramOutputPathConsumer {
private final Path directory;
private boolean preparedDirectory = false;
private final OutputBuilder outputBuilder;
protected final boolean consumeDataResources;
public DirectoryConsumer(Path directory) {
this(directory, null, false);
}
public DirectoryConsumer(Path directory, boolean consumeDataResources) {
this(directory, null, consumeDataResources);
}
public DirectoryConsumer(Path directory, DexIndexedConsumer consumer) {
this(directory, consumer, false);
}
public DirectoryConsumer(
Path directory, DexIndexedConsumer consumer, boolean consumeDataResources) {
super(consumer);
this.directory = directory;
this.outputBuilder = new DirectoryBuilder(directory);
this.consumeDataResources = consumeDataResources;
}
@Override
public DataResourceConsumer getDataResourceConsumer() {
return consumeDataResources ? this : null;
}
@Override
public void accept(
int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
try {
prepareDirectory();
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, new PathOrigin(directory)));
}
outputBuilder.addFile(DexUtils.getDefaultDexFileName(fileIndex), data, handler);
}
@Override
public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
outputBuilder.addDirectory(directory.getName(), handler);
}
@Override
public void accept(DataEntryResource file, DiagnosticsHandler handler) {
outputBuilder.addFile(file.getName(), file, handler);
}
@Override
public void finished(DiagnosticsHandler handler) {
super.finished(handler);
outputBuilder.close(handler);
}
private synchronized void prepareDirectory() throws IOException {
if (preparedDirectory) {
return;
}
preparedDirectory = true;
DexIndexedConsumerUtils.DirectoryConsumerUtils.deleteClassesDexFiles(directory);
}
@Override
public Path internalGetOutputPath() {
return outputBuilder.getPath();
}
}
}