Add API for using a client provided byte-buffer during DEX code writing.
Change-Id: I0a67da420716f51e0caeb2ea5e53bb54e103f8ee
diff --git a/src/main/java/com/android/tools/r8/ByteBufferProvider.java b/src/main/java/com/android/tools/r8/ByteBufferProvider.java
new file mode 100644
index 0000000..cf80266
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ByteBufferProvider.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2018, 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 java.nio.ByteBuffer;
+
+/** Interface to enable manual memory management for a pool of byte buffers. */
+@KeepForSubclassing
+public interface ByteBufferProvider {
+
+ /**
+ * Acquire unique ownership of a {@link ByteBuffer}.
+ *
+ * <p>The buffer must have an array backing and must have capacity of at least the requested
+ * {@param capacity} value. The buffer must be positioned at position zero.
+ *
+ * <p>The buffer is owned by the caller until it is explicitly given back by a call to {@link
+ * ByteBufferProvider::releaseByteBuffer}.
+ *
+ * <p>Requests for byte buffers can happen in parallel with no guarantees of thread or order.
+ */
+ default ByteBuffer acquireByteBuffer(int capacity) {
+ return ByteBuffer.allocate(capacity);
+ }
+
+ /**
+ * Release ownership of a previously acquired byte buffer.
+ *
+ * <p>After a byte buffer is released it is free to be reclaimed or reused by other requests.
+ *
+ * <p>The release of a buffer will only happen once for each acquired buffer and it will happen
+ * only on the same thread that acquired it.
+ */
+ default void releaseByteBuffer(ByteBuffer buffer) {
+ // Implicitly reclaimed by GC.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ByteDataView.java b/src/main/java/com/android/tools/r8/ByteDataView.java
new file mode 100644
index 0000000..65afbb1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ByteDataView.java
@@ -0,0 +1,64 @@
+// Copyright (c) 2018, 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 java.util.Arrays;
+
+/** Byte data view of a buffer that is possibly larger than the data content. */
+@Keep
+public final class ByteDataView {
+ private byte[] buffer;
+ private final int offset;
+ private final int length;
+
+ /** Create a view spanning an entire byte array. */
+ public static ByteDataView of(byte[] data) {
+ return new ByteDataView(data, 0, data.length);
+ }
+
+ /**
+ * Create a view of a byte array.
+ *
+ * <p>The view starts at {@param offset} and runs {@param length} bytes.
+ */
+ public ByteDataView(byte[] buffer, int offset, int length) {
+ assert offset >= 0;
+ assert length >= 0;
+ assert offset + length <= buffer.length;
+ this.buffer = buffer;
+ this.offset = offset;
+ this.length = length;
+ }
+
+ /** Get the full backing buffer of the byte data view. */
+ public byte[] getBuffer() {
+ assert buffer != null;
+ return buffer;
+ }
+
+ /** Get the offset at which the view starts. */
+ public int getOffset() {
+ assert buffer != null;
+ return offset;
+ }
+
+ /**
+ * Get the length of the data content.
+ *
+ * <p>Note that this does not include the offset.
+ */
+ public int getLength() {
+ assert buffer != null;
+ return length;
+ }
+
+ /** Get a copy of the data truncated to the actual data view. */
+ public byte[] copyByteData() {
+ return Arrays.copyOfRange(buffer, offset, offset + length);
+ }
+
+ public void invalidate() {
+ buffer = null;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index 3bab0ab..3521429 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -38,11 +38,14 @@
* {@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 data Java class-file encoded data.
* @param descriptor Class descriptor of the class the data pertains to.
* @param handler Diagnostics handler for reporting.
*/
- void accept(byte[] data, String descriptor, DiagnosticsHandler handler);
+ void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler);
/** Empty consumer to request the production of the resource but ignore its value. */
static ClassFileConsumer emptyConsumer() {
@@ -67,7 +70,7 @@
}
@Override
- public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
if (consumer != null) {
consumer.accept(data, descriptor, handler);
}
@@ -116,7 +119,7 @@
}
@Override
- public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
outputBuilder.addFile(getClassFileName(descriptor), data, handler);
}
@@ -196,7 +199,7 @@
}
@Override
- public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
outputBuilder.addFile(ArchiveConsumer.getClassFileName(descriptor), data, handler);
}
diff --git a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
index 311a3dd..ecc2e1c 100644
--- a/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexFilePerClassFileConsumer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputBuilder;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -30,7 +31,7 @@
* <p>This consumer receives DEX file content for each Java class-file input.
*/
@KeepForSubclassing
-public interface DexFilePerClassFileConsumer extends ProgramConsumer {
+public interface DexFilePerClassFileConsumer extends ProgramConsumer, ByteBufferProvider {
/**
* Callback to receive DEX data for a single Java class-file input and its companion classes.
@@ -41,16 +42,35 @@
* {@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 primaryClassDescriptor Class descriptor of the class from the input class-file.
- * @param data DEX encoded data.
+ * @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.
*/
- void accept(
+ default void accept(
+ String primaryClassDescriptor,
+ 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(primaryClassDescriptor, data.copyByteData(), descriptors, handler);
+ }
+
+ // Any new implementation should not use or call the deprecated accept method.
+ @Deprecated
+ default void accept(
String primaryClassDescriptor,
byte[] data,
Set<String> descriptors,
- DiagnosticsHandler handler);
+ DiagnosticsHandler handler) {
+ handler.error(
+ new StringDiagnostic(
+ "Deprecated use of DexFilePerClassFileConsumer::accept(..., byte[], ...)"));
+ }
/** Empty consumer to request the production of the resource but ignore its value. */
static DexFilePerClassFileConsumer emptyConsumer() {
@@ -77,7 +97,7 @@
@Override
public void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
if (consumer != null) {
@@ -135,7 +155,7 @@
@Override
public void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
super.accept(primaryClassDescriptor, data, descriptors, handler);
@@ -217,14 +237,13 @@
@Override
public void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
super.accept(primaryClassDescriptor, data, descriptors, handler);
outputBuilder.addFile(getDexFileName(primaryClassDescriptor), data, handler);
}
-
@Override
public void accept(DataDirectoryResource directory, DiagnosticsHandler handler) {
outputBuilder.addDirectory(directory.getName(), handler);
diff --git a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
index e802103..118df2e 100644
--- a/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
+++ b/src/main/java/com/android/tools/r8/DexIndexedConsumer.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.OutputBuilder;
+import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
@@ -32,7 +33,7 @@
* than a single DEX file. This is the default consumer for DEX programs.
*/
@KeepForSubclassing
-public interface DexIndexedConsumer extends ProgramConsumer {
+public interface DexIndexedConsumer extends ProgramConsumer, ByteBufferProvider {
/**
* Callback to receive DEX data for a compilation output.
@@ -46,12 +47,28 @@
* {@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.
+ * @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.
*/
- void accept(int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler);
+ 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() {
@@ -87,7 +104,7 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
if (consumer != null) {
consumer.accept(fileIndex, data, descriptors, handler);
}
@@ -141,7 +158,7 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
outputBuilder.addFile(getDexFileName(fileIndex), data, handler);
}
@@ -219,7 +236,7 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
try {
prepareDirectory();
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 807509a..20571ba 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.benchmarks.BenchmarkUtils.printRuntimeNanoseconds;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
@@ -117,11 +118,11 @@
@Override
public synchronized void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
- ProgramResource resource =
- ProgramResource.fromBytes(Origin.unknown(), Kind.DEX, data, descriptors);
+ ProgramResource resource = ProgramResource.fromBytes(
+ Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors);
for (String descriptor : descriptors) {
assert !outputs.containsKey(descriptor);
if (provider.resources.containsKey(descriptor)) {
@@ -159,11 +160,11 @@
@Override
public synchronized void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
- ProgramResource resource =
- ProgramResource.fromBytes(Origin.unknown(), Kind.DEX, data, descriptors);
+ ProgramResource resource = ProgramResource.fromBytes(
+ Origin.unknown(), Kind.DEX, data.copyByteData(), descriptors);
for (String descriptor : descriptors) {
if (provider.resources.containsKey(descriptor)) {
outputs.put(descriptor, resource);
diff --git a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
index 3f241a0..477fd63 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
@@ -30,7 +31,7 @@
@Override
public void accept(
int fileIndex,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
if (fileIndex != 0) {
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index 2621979..3953b22 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.compatdexbuilder;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompatDxHelper;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
@@ -44,10 +45,10 @@
@Override
public synchronized void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
assert bytes == null;
- bytes = data;
+ bytes = data.copyByteData();
}
byte[] getBytes() {
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 7d2907a..7321b53 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.compatdx;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompatDxHelper;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
@@ -24,9 +25,11 @@
import com.android.tools.r8.utils.ZipUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -501,20 +504,21 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
if (fileIndex > 0) {
throw new CompilationError(
"Compilation result could not fit into a single dex file. "
+ "Reduce the input-program size or run with --multi-dex enabled");
}
assert bytes == null;
- bytes = data;
+ // Store a copy of the bytes as we may not assume the backing is valid after accept returns.
+ bytes = data.copyByteData();
}
@Override
public void finished(DiagnosticsHandler handler) {
if (bytes != null) {
- super.accept(0, bytes, null, handler);
+ super.accept(0, ByteDataView.of(bytes), null, handler);
}
super.finished(handler);
}
@@ -530,14 +534,12 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
- try {
- Files.write(
- output,
- data,
- StandardOpenOption.CREATE,
- StandardOpenOption.TRUNCATE_EXISTING,
- StandardOpenOption.WRITE);
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+ StandardOpenOption[] options = {
+ StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING
+ };
+ try (OutputStream stream = new BufferedOutputStream(Files.newOutputStream(output, options))) {
+ stream.write(data.getBuffer(), data.getOffset(), data.getLength());
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, new PathOrigin(output)));
}
@@ -573,8 +575,8 @@
ZipEntry entry = entries.nextElement();
if (ZipUtils.isClassFile(entry.getName())) {
try (InputStream entryStream = zipFile.getInputStream(entry)) {
- outputBuilder.addFile(
- entry.getName(), ByteStreams.toByteArray(entryStream), handler);
+ byte[] bytes = ByteStreams.toByteArray(entryStream);
+ outputBuilder.addFile(entry.getName(), ByteDataView.of(bytes), handler);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index f9e95ec..a2ef57d 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -3,14 +3,19 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.ByteBufferProvider;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DataDirectoryResource;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceConsumer;
import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.DataResourceProvider.Visitor;
+import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
@@ -228,30 +233,40 @@
dexDataFutures.add(
executorService.submit(
() -> {
- byte[] result = writeDexFile(mapping);
+ ProgramConsumer consumer;
+ ByteBufferProvider byteBufferProvider;
if (programConsumer != null) {
- programConsumer.accept(
- virtualFile.getId(),
- result,
- virtualFile.getClassDescriptors(),
- options.reporter);
+ consumer = programConsumer;
+ byteBufferProvider = programConsumer;
} else if (virtualFile.getPrimaryClassDescriptor() != null) {
- options
- .getDexFilePerClassFileConsumer()
+ consumer = options.getDexFilePerClassFileConsumer();
+ byteBufferProvider = options.getDexFilePerClassFileConsumer();
+ } else {
+ consumer = options.getDexIndexedConsumer();
+ byteBufferProvider = options.getDexIndexedConsumer();
+ }
+ ByteBufferResult result = writeDexFile(mapping, byteBufferProvider);
+ ByteDataView data =
+ new ByteDataView(
+ result.buffer.array(), result.buffer.arrayOffset(), result.length);
+ if (consumer instanceof DexFilePerClassFileConsumer) {
+ ((DexFilePerClassFileConsumer) consumer)
.accept(
virtualFile.getPrimaryClassDescriptor(),
- result,
+ data,
virtualFile.getClassDescriptors(),
options.reporter);
} else {
- options
- .getDexIndexedConsumer()
+ ((DexIndexedConsumer) consumer)
.accept(
virtualFile.getId(),
- result,
+ data,
virtualFile.getClassDescriptors(),
options.reporter);
}
+ // Release use of the backing buffer now that accept has returned.
+ data.invalidate();
+ byteBufferProvider.releaseByteBuffer(result.buffer);
return true;
}));
}
@@ -451,8 +466,9 @@
}
}
- private byte[] writeDexFile(ObjectToOffsetMapping mapping) {
- FileWriter fileWriter = new FileWriter(mapping, application, options, namingLens);
+ private ByteBufferResult writeDexFile(
+ ObjectToOffsetMapping mapping, ByteBufferProvider provider) {
+ FileWriter fileWriter = new FileWriter(provider, mapping, application, options, namingLens);
// Collect the non-fixed sections.
fileWriter.collect();
// Generate and write the bytes.
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
index fac5e53..000bc0e 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.ByteBufferProvider;
import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.utils.EncodedValueUtils;
import com.android.tools.r8.utils.LebUtils;
+import com.google.common.annotations.VisibleForTesting;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
@@ -18,28 +21,59 @@
public class DexOutputBuffer {
private static final int DEFAULT_BUFFER_SIZE = 256 * 1024;
+ private final ByteBufferProvider byteBufferProvider;
private ByteBuffer byteBuffer;
- public DexOutputBuffer() {
- byteBuffer = allocate(DEFAULT_BUFFER_SIZE);
+ @VisibleForTesting
+ DexOutputBuffer() {
+ this(new ByteBufferProvider() {});
+ }
+
+ public DexOutputBuffer(ByteBufferProvider byteBufferProvider) {
+ this.byteBufferProvider = byteBufferProvider;
+ byteBuffer = allocateByteBuffer(DEFAULT_BUFFER_SIZE);
}
private void ensureSpaceFor(int bytes) {
if (byteBuffer.remaining() < bytes) {
int newSize = byteBuffer.capacity() + Math.max(byteBuffer.capacity(), bytes * 2);
- ByteBuffer newBuffer = allocate(newSize);
+ ByteBuffer newBuffer = allocateByteBuffer(newSize);
System.arraycopy(byteBuffer.array(), 0, newBuffer.array(), 0, byteBuffer.position());
newBuffer.position(byteBuffer.position());
+ freeByteBuffer(byteBuffer);
byteBuffer = newBuffer;
}
}
- private ByteBuffer allocate(int size) {
- ByteBuffer buffer = ByteBuffer.allocate(size);
+ private ByteBuffer allocateByteBuffer(int size) {
+ ByteBuffer buffer = byteBufferProvider.acquireByteBuffer(size);
+ if (!buffer.hasArray()) {
+ throw new CompilationError(
+ "Provided byte-buffer is required to have an array backing, but does not.");
+ }
+ if (buffer.capacity() < size) {
+ throw new CompilationError(
+ "Insufficient capacity of provided byte-buffer."
+ + " Requested capacity "
+ + size
+ + ", actual capacity: "
+ + buffer.capacity());
+ }
+ if (buffer.position() != 0) {
+ throw new CompilationError(
+ "Provided byte-buffer is required to start at position zero, but starts at "
+ + buffer.position()
+ + ".");
+ }
buffer.order(ByteOrder.LITTLE_ENDIAN);
return buffer;
}
+ private void freeByteBuffer(ByteBuffer buffer) {
+ assert buffer != null;
+ byteBufferProvider.releaseByteBuffer(buffer);
+ }
+
public void putUleb128(int value) {
LebUtils.putUleb128(this, value);
}
@@ -130,4 +164,10 @@
public byte[] asArray() {
return byteBuffer.array();
}
+
+ public ByteBuffer stealByteBuffer() {
+ ByteBuffer buffer = byteBuffer;
+ byteBuffer = null;
+ return buffer;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index b73f8a4..f1b14b3 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.LebUtils.sizeAsUleb128;
import com.android.tools.r8.ApiLevelException;
+import com.android.tools.r8.ByteBufferProvider;
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.Descriptor;
@@ -56,6 +57,7 @@
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
+import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Collection;
@@ -72,14 +74,27 @@
public class FileWriter {
+ /** Simple pair of a byte buffer and its written length. */
+ public static class ByteBufferResult {
+ // Ownership of the buffer is transferred to the receiver of this result structure.
+ public final ByteBuffer buffer;
+ public final int length;
+
+ private ByteBufferResult(ByteBuffer buffer, int length) {
+ this.buffer = buffer;
+ this.length = length;
+ }
+ }
+
private final ObjectToOffsetMapping mapping;
private final DexApplication application;
private final InternalOptions options;
private final NamingLens namingLens;
- private final DexOutputBuffer dest = new DexOutputBuffer();
+ private final DexOutputBuffer dest;
private final MixedSectionOffsets mixedSectionOffsets;
public FileWriter(
+ ByteBufferProvider provider,
ObjectToOffsetMapping mapping,
DexApplication application,
InternalOptions options,
@@ -88,6 +103,7 @@
this.application = application;
this.options = options;
this.namingLens = namingLens;
+ this.dest = new DexOutputBuffer(provider);
this.mixedSectionOffsets = new MixedSectionOffsets(options);
}
@@ -134,7 +150,7 @@
return this;
}
- public byte[] generate() {
+ public ByteBufferResult generate() {
// Check restrictions on interface methods.
checkInterfaceMethods();
@@ -198,8 +214,8 @@
writeSignature(layout);
writeChecksum(layout);
- // Turn into an array
- return Arrays.copyOf(dest.asArray(), layout.getEndOfFile());
+ // Wrap backing buffer with actual length.
+ return new ByteBufferResult(dest.stealByteBuffer(), layout.getEndOfFile());
}
private void checkInterfaceMethods() {
diff --git a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
index ba576e7..8b0f6a8 100644
--- a/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
+++ b/src/main/java/com/android/tools/r8/dexfilemerger/DexFileMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.dexfilemerger;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.DexFileMergerHelper;
@@ -210,12 +211,14 @@
@Override
public synchronized void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
if (singleFixedFileIndex != null && fileIndex != 0) {
handler.error(new StringDiagnostic("Result does not fit into a single dex file."));
return;
}
- writers.put(fileIndex, () -> writeEntry(fileIndex, data, descriptors, handler));
+ // Make a copy of the actual bytes as they will possibly be accessed later by the runner.
+ final byte[] bytes = data.copyByteData();
+ writers.put(fileIndex, () -> writeEntry(fileIndex, bytes, descriptors, handler));
while (writers.containsKey(highestIndexWritten + 1)) {
++highestIndexWritten;
@@ -243,7 +246,11 @@
int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
try {
ZipUtils.writeToZipStream(
- getStream(handler), getDexFileName(fileIndex), data, ZipEntry.DEFLATED, true);
+ getStream(handler),
+ getDexFileName(fileIndex),
+ ByteDataView.of(data),
+ ZipEntry.DEFLATED,
+ true);
hasWrittenSomething = true;
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, origin));
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 6537667..9562ac1 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -5,6 +5,7 @@
import static org.objectweb.asm.Opcodes.ASM6;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.errors.Unimplemented;
@@ -168,7 +169,7 @@
verifyCf(result);
}
ExceptionUtils.withConsumeResourceHandler(
- options.reporter, handler -> consumer.accept(result, desc, handler));
+ options.reporter, handler -> consumer.accept(ByteDataView.of(result), desc, handler));
}
private int getClassFileVersion(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
index 14bf6f5..4371edd 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidAppConsumers.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.BaseCompilerCommand;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
@@ -84,9 +85,12 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex,
+ ByteDataView data,
+ Set<String> descriptors,
+ DiagnosticsHandler handler) {
super.accept(fileIndex, data, descriptors, handler);
- addDexFile(fileIndex, data, descriptors);
+ addDexFile(fileIndex, data.copyByteData(), descriptors);
}
@Override
@@ -121,11 +125,11 @@
@Override
public void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
super.accept(primaryClassDescriptor, data, descriptors, handler);
- addDexFile(primaryClassDescriptor, data, descriptors);
+ addDexFile(primaryClassDescriptor, data.copyByteData(), descriptors);
}
synchronized void addDexFile(
@@ -157,9 +161,9 @@
private List<DescriptorsWithContents> files = new ArrayList<>();
@Override
- public void accept(byte[] data, String descriptor, DiagnosticsHandler handler) {
+ public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
- addClassFile(data, descriptor);
+ addClassFile(data.copyByteData(), descriptor);
}
synchronized void addClassFile(byte[] data, String descriptor) {
diff --git a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
index 1a64ab0..a656a58 100644
--- a/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/ArchiveBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResource;
import com.android.tools.r8.DiagnosticsHandler;
@@ -103,7 +104,7 @@
@Override
public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
try (InputStream in = content.getByteStream()) {
- addFile(name, ByteStreams.toByteArray(in), handler);
+ addFile(name, ByteDataView.of(ByteStreams.toByteArray(in)), handler);
} catch (IOException e) {
handleIOException(e, handler);
} catch (ResourceException e) {
@@ -112,8 +113,8 @@
}
}
- @Override
- public synchronized void addFile(String name, byte[] content, DiagnosticsHandler handler) {
+ @Override
+ public synchronized void addFile(String name, ByteDataView content, DiagnosticsHandler handler) {
try {
ZipUtils.writeToZipStream(getStream(handler), name, content, ZipEntry.STORED);
} catch (IOException e) {
diff --git a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
index 2058d39..fc5ee55 100644
--- a/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/DirectoryBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.ResourceException;
@@ -46,7 +47,7 @@
@Override
public void addFile(String name, DataEntryResource content, DiagnosticsHandler handler) {
try (InputStream in = content.getByteStream()) {
- addFile(name, ByteStreams.toByteArray(in), handler);
+ addFile(name, ByteDataView.of(ByteStreams.toByteArray(in)), handler);
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, content.getOrigin()));
} catch (ResourceException e) {
@@ -56,7 +57,7 @@
}
@Override
- public synchronized void addFile(String name, byte[] content, DiagnosticsHandler handler) {
+ public synchronized void addFile(String name, ByteDataView content, DiagnosticsHandler handler) {
Path target = root.resolve(name.replace(NAME_SEPARATOR, File.separatorChar));
try {
Files.createDirectories(target.getParent());
diff --git a/src/main/java/com/android/tools/r8/utils/FileUtils.java b/src/main/java/com/android/tools/r8/utils/FileUtils.java
index feb57a5..b64f9ac 100644
--- a/src/main/java/com/android/tools/r8/utils/FileUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/FileUtils.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.ByteDataView;
import com.google.common.io.Closer;
import java.io.IOException;
import java.io.OutputStream;
@@ -148,6 +149,11 @@
public static void writeToFile(Path output, OutputStream defValue, byte[] contents)
throws IOException {
+ writeToFile(output, defValue, ByteDataView.of(contents));
+ }
+
+ public static void writeToFile(Path output, OutputStream defValue, ByteDataView contents)
+ throws IOException {
try (Closer closer = Closer.create()) {
OutputStream outputStream =
openPathWithDefault(
@@ -157,7 +163,7 @@
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE);
- outputStream.write(contents);
+ outputStream.write(contents.getBuffer(), contents.getOffset(), contents.getLength());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
index 9237485..9077f7b 100644
--- a/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/OutputBuilder.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.origin.Origin;
@@ -20,7 +21,7 @@
void addFile(String name, DataEntryResource content, DiagnosticsHandler handler);
- void addFile(String name, byte[] content, DiagnosticsHandler handler);
+ void addFile(String name, ByteDataView content, DiagnosticsHandler handler);
Path getPath();
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index fd50583..740bf9c 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.FileUtils.DEX_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.MODULE_INFO_CLASS;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.errors.CompilationError;
import com.google.common.io.ByteStreams;
import java.io.File;
@@ -73,27 +74,36 @@
public static void writeToZipStream(
ZipOutputStream stream, String entry, byte[] content, int compressionMethod)
throws IOException {
+ writeToZipStream(stream, entry, ByteDataView.of(content), compressionMethod, false);
+ }
+
+ public static void writeToZipStream(
+ ZipOutputStream stream, String entry, ByteDataView content, int compressionMethod)
+ throws IOException {
writeToZipStream(stream, entry, content, compressionMethod, false);
}
public static void writeToZipStream(
ZipOutputStream stream,
String entry,
- byte[] content,
+ ByteDataView content,
int compressionMethod,
boolean setZeroTime)
throws IOException {
+ byte[] buffer = content.getBuffer();
+ int offset = content.getOffset();
+ int length = content.getLength();
CRC32 crc = new CRC32();
- crc.update(content);
+ crc.update(buffer, offset, length);
ZipEntry zipEntry = new ZipEntry(entry);
zipEntry.setMethod(compressionMethod);
- zipEntry.setSize(content.length);
+ zipEntry.setSize(length);
zipEntry.setCrc(crc.getValue());
if (setZeroTime) {
zipEntry.setTime(0);
}
stream.putNextEntry(zipEntry);
- stream.write(content);
+ stream.write(buffer, offset, length);
stream.closeEntry();
}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index 87453d0..f23eb7b 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -367,16 +367,15 @@
class MultiTypeConsumer implements DexIndexedConsumer, DexFilePerClassFileConsumer {
@Override
- public void accept(String primaryClassDescriptor, byte[] data, Set<String> descriptors,
- DiagnosticsHandler handler) {
-
- }
+ public void accept(
+ String primaryClassDescriptor,
+ ByteDataView data,
+ Set<String> descriptors,
+ DiagnosticsHandler handler) {}
@Override
- public void accept(int fileIndex, byte[] data, Set<String> descriptors,
- DiagnosticsHandler handler) {
-
- }
+ public void accept(
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {}
@Override
public void finished(DiagnosticsHandler handler) {
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index 93a43f7..a063ab2 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -27,12 +27,12 @@
@Override
public void accept(
int fileIndex,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
Marker marker;
try {
- marker = ExtractMarker.extractMarkerFromDexProgramData(data);
+ marker = ExtractMarker.extractMarkerFromDexProgramData(data.copyByteData());
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index 6533e08..6310054 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -36,7 +36,7 @@
@Override
public void accept(
int fileIndex,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {}
diff --git a/src/test/java/com/android/tools/r8/R8CFExamplesTests.java b/src/test/java/com/android/tools/r8/R8CFExamplesTests.java
index 2dbfb37..bb88036 100644
--- a/src/test/java/com/android/tools/r8/R8CFExamplesTests.java
+++ b/src/test/java/com/android/tools/r8/R8CFExamplesTests.java
@@ -70,7 +70,7 @@
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ClassFileConsumer inputConsumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
- inputConsumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+ inputConsumer.accept(ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), descriptor, null);
inputConsumer.finished(null);
return inputJar;
}
@@ -82,7 +82,8 @@
Path path = testDirectory.resolve(className.replace('.', '/') + ".class");
String descriptor = DescriptorUtils.javaTypeToDescriptor(className);
try (InputStream inputStream = new FileInputStream(path.toFile())) {
- inputConsumer.accept(ByteStreams.toByteArray(inputStream), descriptor, null);
+ inputConsumer.accept(
+ ByteDataView.of(ByteStreams.toByteArray(inputStream)), descriptor, null);
}
}
inputConsumer.finished(null);
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 180a5f8..706ac89 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1487,8 +1487,8 @@
@Override
public synchronized void accept(
- byte[] data, String descriptor, DiagnosticsHandler handler) {
- builder.addClassProgramData(data, cfOrigin);
+ ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+ builder.addClassProgramData(data.copyByteData(), cfOrigin);
}
@Override
diff --git a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
index 7d88649..4713369 100644
--- a/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/AlwaysNullGetItemTestRunner.java
@@ -5,9 +5,10 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
@@ -64,7 +65,7 @@
Path out = temp.getRoot().toPath().resolve("aaload-null.jar");
ClassFileConsumer.ArchiveConsumer archiveConsumer = new ClassFileConsumer.ArchiveConsumer(out);
archiveConsumer.accept(
- AlwaysNullGetItemDump.dump(),
+ ByteDataView.of(AlwaysNullGetItemDump.dump()),
DescriptorUtils.javaTypeToDescriptor(CLASS.getCanonicalName()),
null);
archiveConsumer.finished(null);
diff --git a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
index da32cce..97997fe 100644
--- a/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/MethodHandleTestRunner.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DexIndexedConsumer;
@@ -111,7 +112,9 @@
ClassFileConsumer.ArchiveConsumer archiveConsumer = new ClassFileConsumer.ArchiveConsumer(out);
for (Class<?> c : inputClasses) {
archiveConsumer.accept(
- getClassAsBytes(c), DescriptorUtils.javaTypeToDescriptor(c.getName()), null);
+ ByteDataView.of(getClassAsBytes(c)),
+ DescriptorUtils.javaTypeToDescriptor(c.getName()),
+ null);
}
archiveConsumer.finished(null);
String expected = lookupType == LookupType.CONSTANT ? "error" : "exception";
diff --git a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
index f433a9d..03c28cd 100644
--- a/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/UninitializedInFrameTestRunner.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
@@ -40,7 +41,8 @@
Path output = temp.getRoot().toPath().resolve("output.jar");
ArchiveConsumer inputConsumer = new ArchiveConsumer(input);
- inputConsumer.accept(clazz, DescriptorUtils.javaTypeToDescriptor(CLASS.getName()), null);
+ inputConsumer.accept(
+ ByteDataView.of(clazz), DescriptorUtils.javaTypeToDescriptor(CLASS.getName()), null);
inputConsumer.finished(null);
ProcessResult runInput = ToolHelper.runJava(input, CLASS.getCanonicalName());
if (runInput.exitCode != 0) {
diff --git a/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java b/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java
index 63f131e..9949a68 100644
--- a/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java
+++ b/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.R8;
@@ -57,7 +58,7 @@
public void test() throws Exception {
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ArchiveConsumer consumer = new ArchiveConsumer(inputJar);
- consumer.accept(Dump.dump(), DESCRIPTOR, null);
+ consumer.accept(ByteDataView.of(Dump.dump()), DESCRIPTOR, null);
consumer.finished(null);
ProcessResult runInput = ToolHelper.runJava(inputJar, CLASS_NAME);
assertEquals(0, runInput.exitCode);
diff --git a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
index 59034a5..f413f98 100644
--- a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
+++ b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
@@ -77,7 +78,7 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
hasOutput = true;
}
diff --git a/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java
index bcc70c6..41a9fd4 100644
--- a/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/ArrayDimensionGreaterThanSevenTestRunner.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.OutputMode;
@@ -72,7 +73,7 @@
Path out = temp.getRoot().toPath().resolve("out.jar");
ArchiveConsumer consumer = new ArchiveConsumer(out);
consumer.accept(
- ArrayDimensionGreaterThanSevenTestDump.dump(),
+ ByteDataView.of(ArrayDimensionGreaterThanSevenTestDump.dump()),
DescriptorUtils.javaTypeToDescriptor(NAME),
null);
consumer.finished(null);
diff --git a/src/test/java/com/android/tools/r8/debug/IincDebugTestRunner.java b/src/test/java/com/android/tools/r8/debug/IincDebugTestRunner.java
index a275e6b..c7a5b16 100644
--- a/src/test/java/com/android/tools/r8/debug/IincDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/IincDebugTestRunner.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
@@ -90,7 +91,7 @@
private Path buildInput(byte[] clazz) {
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ArchiveConsumer inputJarConsumer = new ArchiveConsumer(inputJar);
- inputJarConsumer.accept(clazz, IincDebugTestDump.DESCRIPTOR, null);
+ inputJarConsumer.accept(ByteDataView.of(clazz), IincDebugTestDump.DESCRIPTOR, null);
inputJarConsumer.finished(null);
return inputJar;
}
diff --git a/src/test/java/com/android/tools/r8/debug/LocalEndTestRunner.java b/src/test/java/com/android/tools/r8/debug/LocalEndTestRunner.java
index 982332e..3f86969 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalEndTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalEndTestRunner.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -40,7 +41,7 @@
if (inputJarCache == null) {
inputJarCache = temp.getRoot().toPath().resolve("input.jar");
ClassFileConsumer jarWriter = new ArchiveConsumer(inputJarCache);
- jarWriter.accept(LocalEndTestDump.dump(), DESC, null);
+ jarWriter.accept(ByteDataView.of(LocalEndTestDump.dump()), DESC, null);
jarWriter.finished(null);
}
return inputJarCache;
diff --git a/src/test/java/com/android/tools/r8/debug/NopDebugTestRunner.java b/src/test/java/com/android/tools/r8/debug/NopDebugTestRunner.java
index 813a873..cf341fd 100644
--- a/src/test/java/com/android/tools/r8/debug/NopDebugTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debug/NopDebugTestRunner.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.debug;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.OutputMode;
@@ -31,7 +32,7 @@
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ClassFileConsumer inputConsumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
- inputConsumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+ inputConsumer.accept(ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), descriptor, null);
inputConsumer.finished(null);
return inputJar;
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
index 80279d5..76d2291 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/KotlinDebugInfoTestRunner.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
@@ -21,7 +22,7 @@
private Path buildInput(byte[] clazz, String descriptor) {
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ArchiveConsumer inputJarConsumer = new ArchiveConsumer(inputJar);
- inputJarConsumer.accept(clazz, descriptor, null);
+ inputJarConsumer.accept(ByteDataView.of(clazz), descriptor, null);
inputJarConsumer.finished(null);
return inputJar;
}
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index 3b12cc4..a518a36 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.dex;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.code.ConstString;
@@ -34,10 +35,14 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -161,10 +166,45 @@
private final List<Set<String>> descriptors = new ArrayList<>();
+ private final Deque<ByteBuffer> freeBuffers = new ArrayDeque<>();
+ private final Set<ByteBuffer> activeBuffers = Sets.newIdentityHashSet();
+
+ @Override
+ public ByteBuffer acquireByteBuffer(int capacity) {
+ synchronized (freeBuffers) {
+ ByteBuffer buffer = freeBuffers.pollFirst();
+ // Ensure the buffer has sufficient capacity, eg, skip buffers that are too small.
+ if (buffer != null && buffer.capacity() < capacity) {
+ List<ByteBuffer> small = new ArrayList<>(freeBuffers.size());
+ do {
+ small.add(buffer);
+ buffer = freeBuffers.pollFirst();
+ } while (buffer != null && buffer.capacity() < capacity);
+ freeBuffers.addAll(small);
+ }
+ if (buffer == null) {
+ buffer = ByteBuffer.allocate(capacity);
+ }
+ assert !activeBuffers.contains(buffer);
+ activeBuffers.add(buffer);
+ return buffer;
+ }
+ }
+
+ @Override
+ public void releaseByteBuffer(ByteBuffer buffer) {
+ synchronized (freeBuffers) {
+ assert activeBuffers.contains(buffer);
+ activeBuffers.remove(buffer);
+ buffer.position(0);
+ freeBuffers.offerFirst(buffer);
+ }
+ }
+
@Override
public void accept(
String primaryClassDescriptor,
- byte[] data,
+ ByteDataView data,
Set<String> descriptors,
DiagnosticsHandler handler) {
addDescriptors(descriptors);
diff --git a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
index ba43a10..e6ec726 100644
--- a/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
+++ b/src/test/java/com/android/tools/r8/ir/PhiDefinitionsTestRunner.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.DexIndexedConsumer;
@@ -29,7 +30,7 @@
ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(originalJar);
for (Class clazz : PhiDefinitionsTest.CLASSES) {
String descriptor = DescriptorUtils.javaTypeToDescriptor(clazz.getName());
- consumer.accept(ToolHelper.getClassAsBytes(clazz), descriptor, null);
+ consumer.accept(ByteDataView.of(ToolHelper.getClassAsBytes(clazz)), descriptor, null);
}
consumer.finished(null);
runOriginalJar(originalJar);
@@ -56,9 +57,9 @@
Path dumpJar = temp.getRoot().toPath().resolve("dump.jar");
ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(dumpJar);
String desc = 'L' + PhiDefinitionsTestDump.INTERNAL_NAME + ';';
- consumer.accept(PhiDefinitionsTestDump.dump(), desc, null);
+ consumer.accept(ByteDataView.of(PhiDefinitionsTestDump.dump()), desc, null);
String innerDesc = 'L' + PhiDefinitionsTestDump.INNER_INTERNAL_NAME + ';';
- consumer.accept(PhiDefinitionsTestDump.dumpInner(), innerDesc, null);
+ consumer.accept(ByteDataView.of(PhiDefinitionsTestDump.dumpInner()), innerDesc, null);
consumer.finished(null);
runOriginalJar(dumpJar);
return dumpJar;
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
index 9c15ce7..97d0de1 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminBuilder.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.utils.DescriptorUtils.getPathFromDescriptor;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.ApplicationReader;
@@ -395,7 +396,7 @@
public void writeClassFiles(ClassFileConsumer consumer, DiagnosticsHandler handler)
throws Exception {
for (ClassBuilder clazz : classes) {
- consumer.accept(compile(clazz), clazz.getDescriptor(), handler);
+ consumer.accept(ByteDataView.of(compile(clazz)), clazz.getDescriptor(), handler);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
index 02bca3a..66f1d30 100644
--- a/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
+++ b/src/test/java/com/android/tools/r8/naming/LambdaRenamingTestRunner.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
@@ -36,7 +37,7 @@
ArchiveConsumer buildInput = new ArchiveConsumer(inputJar);
for (Class clazz : CLASSES) {
buildInput.accept(
- ToolHelper.getClassAsBytes(clazz),
+ ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
DescriptorUtils.javaTypeToDescriptor(clazz.getName()),
null);
}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
index e6ac9f8..34e4f3e 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress37740372.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
import com.android.tools.r8.D8Command.Builder;
@@ -142,9 +143,9 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
assertEquals(0, fileIndex);
- this.data = data;
+ this.data = data.copyByteData();
}
}
diff --git a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232.java b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232.java
index fa4e3e1..2bc0dc6 100644
--- a/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232.java
+++ b/src/test/java/com/android/tools/r8/regress/b78493232/Regress78493232.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.regress.b78493232;
import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
@@ -30,9 +31,10 @@
? Paths.get(args[0])
: Paths.get("Regress78493232.jar");
ArchiveConsumer consumer = new ArchiveConsumer(output);
- consumer.accept(Regress78493232Dump.dump(), Regress78493232Dump.CLASS_DESC, null);
consumer.accept(
- ToolHelper.getClassAsBytes(Regress78493232Utils.class),
+ ByteDataView.of(Regress78493232Dump.dump()), Regress78493232Dump.CLASS_DESC, null);
+ consumer.accept(
+ ByteDataView.of(ToolHelper.getClassAsBytes(Regress78493232Utils.class)),
Regress78493232Dump.UTILS_CLASS_DESC,
null);
consumer.finished(null);
diff --git a/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
index 12ef651..c7ab1b7 100644
--- a/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/InstantiatedLambdasTestRunner.java
@@ -5,6 +5,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.CompilationMode;
@@ -35,7 +36,7 @@
ArchiveConsumer buildInput = new ArchiveConsumer(inputJar);
for (Class clazz : CLASSES) {
buildInput.accept(
- ToolHelper.getClassAsBytes(clazz),
+ ByteDataView.of(ToolHelper.getClassAsBytes(clazz)),
DescriptorUtils.javaTypeToDescriptor(clazz.getName()),
null);
}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
index ba0d468..1197c31 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/InliningClassVersionTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
@@ -125,9 +126,11 @@
Path inputJar = temp.getRoot().toPath().resolve("input.jar");
ClassFileConsumer consumer = new ClassFileConsumer.ArchiveConsumer(inputJar);
consumer.accept(
- downgradeClass(ToolHelper.getClassAsBytes(Base.class), OLD_VERSION), BASE_DESCRIPTOR, null);
+ ByteDataView.of(downgradeClass(ToolHelper.getClassAsBytes(Base.class), OLD_VERSION)),
+ BASE_DESCRIPTOR,
+ null);
consumer.accept(
- ToolHelper.getClassAsBytes(Inlinee.class),
+ ByteDataView.of(ToolHelper.getClassAsBytes(Inlinee.class)),
DescriptorUtils.javaTypeToDescriptor(Inlinee.class.getName()),
null);
consumer.finished(null);
diff --git a/src/test/java/com/android/tools/r8/utils/Smali.java b/src/test/java/com/android/tools/r8/utils/Smali.java
index ac46dfc..fb61a57 100644
--- a/src/test/java/com/android/tools/r8/utils/Smali.java
+++ b/src/test/java/com/android/tools/r8/utils/Smali.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.dex.ApplicationReader;
@@ -132,8 +133,8 @@
@Override
public void accept(
- int fileIndex, byte[] data, Set<String> descriptors, DiagnosticsHandler handler) {
- contents = data;
+ int fileIndex, ByteDataView data, Set<String> descriptors, DiagnosticsHandler handler) {
+ contents = data.copyByteData();
}
@Override