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