Merge "Reproduce b/112452064."
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/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index 734dc4a..d939dbe 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -72,12 +72,14 @@
throws IOException, ResourceException {
if (FileUtils.isVDexFile(file)) {
PathOrigin vdexOrigin = new PathOrigin(file);
- VDexReader vdexReader = new VDexReader(vdexOrigin, Files.newInputStream(file));
- VDexParser vDexParser = new VDexParser(vdexReader);
- int index = 0;
- for (byte[] bytes : vDexParser.getDexFiles()) {
- appBuilder.addDexProgramData(bytes, new VdexOrigin(vdexOrigin, index));
- index++;
+ try (InputStream fileInputStream = Files.newInputStream(file)) {
+ VDexReader vdexReader = new VDexReader(vdexOrigin, fileInputStream);
+ VDexParser vDexParser = new VDexParser(vdexReader);
+ int index = 0;
+ for (byte[] bytes : vDexParser.getDexFiles()) {
+ appBuilder.addDexProgramData(bytes, new VdexOrigin(vdexOrigin, index));
+ index++;
+ }
}
} else {
appBuilder.addProgramFiles(file);
diff --git a/src/main/java/com/android/tools/r8/JarDiff.java b/src/main/java/com/android/tools/r8/JarDiff.java
index dad57d4..025f637 100644
--- a/src/main/java/com/android/tools/r8/JarDiff.java
+++ b/src/main/java/com/android/tools/r8/JarDiff.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.google.common.io.ByteStreams.toByteArray;
-
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexClass;
@@ -14,6 +12,7 @@
import com.android.tools.r8.graph.JarClassFileReader;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StreamUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.file.Path;
@@ -135,7 +134,8 @@
private byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor)
throws Exception {
- return toByteArray(inputJar.getProgramResource(descriptor).getByteStream());
+ return StreamUtils.StreamToByteArrayClose(
+ inputJar.getProgramResource(descriptor).getByteStream());
}
private DexProgramClass getDexProgramClass(Path path, byte[] bytes) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 6f9464c..212264d 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.3.14-dev";
+ public static final String LABEL = "1.3.15-dev";
private Version() {
}
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..f4f3c7e 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() {
@@ -134,7 +135,7 @@
ZipEntry entry = entries.nextElement();
if (!entry.getName().endsWith(".class")) {
try (InputStream stream = zipFile.getInputStream(entry)) {
- addEntry(entry.getName(), stream, entry.getTime(), out);
+ addEntry(entry.getName(), stream, out);
}
} else {
toDex.add(entry);
@@ -149,7 +150,7 @@
for (int i = 0; i < futures.size(); i++) {
ZipEntry entry = toDex.get(i);
DexConsumer consumer = futures.get(i).get();
- addEntry(entry.getName() + ".dex", consumer.getBytes(), entry.getTime(), out);
+ addEntry(entry.getName() + ".dex", consumer.getBytes(), out);
}
}
} finally {
@@ -177,20 +178,19 @@
return consumer;
}
- private static void addEntry(String name, InputStream stream, long time, ZipOutputStream out)
+ private static void addEntry(String name, InputStream stream, ZipOutputStream out)
throws IOException {
- addEntry(name, ByteStreams.toByteArray(stream), time, out);
+ addEntry(name, ByteStreams.toByteArray(stream), out);
}
- private static void addEntry(String name, byte[] bytes, long time, ZipOutputStream out)
- throws IOException {
+ private static void addEntry(String name, byte[] bytes, ZipOutputStream out) throws IOException {
ZipEntry zipEntry = new ZipEntry(name);
CRC32 crc32 = new CRC32();
crc32.update(bytes);
zipEntry.setSize(bytes.length);
zipEntry.setMethod(ZipEntry.STORED);
zipEntry.setCrc(crc32.getValue());
- zipEntry.setTime(time);
+ zipEntry.setTime(0);
out.putNextEntry(zipEntry);
out.write(bytes);
out.closeEntry();
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/BinaryReader.java b/src/main/java/com/android/tools/r8/dex/BinaryReader.java
index 127e1d5..5d51238 100644
--- a/src/main/java/com/android/tools/r8/dex/BinaryReader.java
+++ b/src/main/java/com/android/tools/r8/dex/BinaryReader.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.LebUtils;
-import com.google.common.io.ByteStreams;
+import com.android.tools.r8.utils.StreamUtils;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -20,7 +20,7 @@
protected final ByteBuffer buffer;
protected BinaryReader(ProgramResource resource) throws ResourceException, IOException {
- this(resource.getOrigin(), ByteStreams.toByteArray(resource.getByteStream()));
+ this(resource.getOrigin(), StreamUtils.StreamToByteArrayClose(resource.getByteStream()));
}
protected BinaryReader(Origin origin, byte[] 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..b5db1ae 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,10 @@
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);
hasWrittenSomething = true;
} catch (IOException e) {
handler.error(new ExceptionDiagnostic(e, origin));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index f92eb80..a38e22b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -70,8 +70,8 @@
Instruction replacement = null;
ValueType valueType = instruction.outValue().outType();
if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
- assert valueType != ValueType.OBJECT;
Value value = code.createValue(valueType, instruction.getLocalInfo());
+ assert valueType != ValueType.OBJECT || rule.getReturnValue().isNull();
replacement = new ConstNumber(value, rule.getReturnValue().getSingleValue());
}
if (replacement == null &&
@@ -132,12 +132,12 @@
DexEncodedMethod definition = appInfo
.lookup(invoke.getType(), invokedMethod, callingContext);
- // Process invokes marked as having no side effects.
boolean invokeReplaced = false;
ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
if (lookup != null) {
if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
&& (invoke.outValue() == null || !invoke.outValue().isUsed())) {
+ // Remove invoke if marked as having no side effects and the return value is not used.
iterator.remove();
invokeReplaced = true;
} else if (invoke.outValue() != null && invoke.outValue().isUsed()) {
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/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index d1a0b3d..740aa2c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -968,6 +968,8 @@
ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
} else if (acceptString("false")) {
ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
+ } else if (acceptString("null")) {
+ ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue());
} else {
TextPosition fieldOrValueStart = getPosition();
String qualifiedFieldNameOrInteger = acceptFieldNameOrIntegerForReturn();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index b4e9596..a1aad20 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -8,39 +8,60 @@
import com.android.tools.r8.utils.LongInterval;
public class ProguardMemberRuleReturnValue {
+ public enum Type {
+ BOOLEAN,
+ VALUE_RANGE,
+ FIELD,
+ NULL
+ }
+ private final Type type;
private final boolean booleanValue;
private final LongInterval longInterval;
private final DexField field;
ProguardMemberRuleReturnValue(boolean value) {
+ this.type = Type.BOOLEAN;
this.booleanValue = value;
this.longInterval = null;
this.field = null;
}
ProguardMemberRuleReturnValue(LongInterval value) {
+ this.type = Type.VALUE_RANGE;
this.booleanValue = false;
this.longInterval = value;
this.field = null;
}
ProguardMemberRuleReturnValue(DexField field) {
+ this.type = Type.FIELD;
this.booleanValue = false;
this.longInterval = null;
this.field = field;
}
+ ProguardMemberRuleReturnValue() {
+ this.type = Type.NULL;
+ this.booleanValue = false;
+ this.longInterval = null;
+ this.field = null;
+ }
+
public boolean isBoolean() {
- return longInterval == null && field == null;
+ return type == Type.BOOLEAN;
}
public boolean isValueRange() {
- return longInterval != null && field == null;
+ return type == Type.VALUE_RANGE;
}
public boolean isField() {
- return field != null;
+ return type == Type.FIELD;
+ }
+
+ public boolean isNull() {
+ return type == Type.NULL;
}
public boolean getBoolean() {
@@ -51,22 +72,27 @@
/**
* Returns if this return value is a single value.
*
- * Boolean values are considered a single value.
+ * Boolean values and null are considered a single value.
*/
public boolean isSingleValue() {
- return isBoolean() || (isValueRange() && longInterval.isSingleValue());
+ return isBoolean() || isNull() || (isValueRange() && longInterval.isSingleValue());
}
/**
* Returns the return value.
*
* Boolean values are returned as 0 for <code>false</code> and 1 for <code>true</code>.
+ *
+ * Reference value <code>null</code> is returned as 0.
*/
public long getSingleValue() {
assert isSingleValue();
if (isBoolean()) {
return booleanValue ? 1 : 0;
}
+ if (isNull()) {
+ return 0;
+ }
return longInterval.getSingleValue();
}
@@ -86,6 +112,8 @@
result.append(" return ");
if (isBoolean()) {
result.append(booleanValue ? "true" : "false");
+ } else if (isNull()) {
+ result.append("null");
} else if (isValueRange()) {
result.append(longInterval.getMin());
if (!isSingleValue()) {
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..4111bed 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;
@@ -89,6 +90,7 @@
name += DataResource.SEPARATOR;
}
ZipEntry entry = new ZipEntry(name);
+ entry.setTime(0);
ZipOutputStream zip = getStream(handler);
synchronized (this) {
try {
@@ -103,7 +105,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 +114,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/StreamUtils.java b/src/main/java/com/android/tools/r8/utils/StreamUtils.java
new file mode 100644
index 0000000..8d4ebc3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/StreamUtils.java
@@ -0,0 +1,20 @@
+// 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.utils;
+
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class StreamUtils {
+ /**
+ * Read all data from the stream into a byte[], close the stream and return the bytes.
+ * @return The bytes of the stream
+ */
+ public static byte[] StreamToByteArrayClose(InputStream inputStream) throws IOException {
+ byte[] result = ByteStreams.toByteArray(inputStream);
+ inputStream.close();
+ return result;
+ }
+}
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..97f3699 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,24 @@
public static void writeToZipStream(
ZipOutputStream stream, String entry, byte[] content, int compressionMethod)
throws IOException {
- writeToZipStream(stream, entry, content, compressionMethod, false);
+ writeToZipStream(stream, entry, ByteDataView.of(content), compressionMethod);
}
public static void writeToZipStream(
- ZipOutputStream stream,
- String entry,
- byte[] content,
- int compressionMethod,
- boolean setZeroTime)
+ ZipOutputStream stream, String entry, ByteDataView content, int compressionMethod)
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);
- }
+ zipEntry.setTime(0);
stream.putNextEntry(zipEntry);
- stream.write(content);
+ stream.write(buffer, offset, length);
stream.closeEntry();
}
diff --git a/src/test/examples/assumenosideeffects6/Assumenosideeffects.java b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
new file mode 100644
index 0000000..07630b9
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
@@ -0,0 +1,28 @@
+// 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 assumenosideeffects6;
+
+class A {
+ @CheckDiscarded
+ public static Object methodStaticNull() {
+ System.out.println("methodStaticNull");
+ return new Object();
+ }
+
+ // This method must be on a class which is not explicitly kept (as Assumenosideeffects is).
+ // For an explicitly kept class we cannot expect single target for any methods, so the rule
+ // will never apply (see b/70550443#comment2).
+ @CheckDiscarded
+ public Object methodNull() {
+ System.out.println("methodNull");
+ return new Object();
+ }
+}
+
+public class Assumenosideeffects {
+ public static void main(String[] args) {
+ System.out.println(A.methodStaticNull() != null ? "NOT NULL" : "NULL");
+ System.out.println((new A().methodNull()) != null ? "NOT NULL" : "NULL");
+ }
+}
diff --git a/src/test/examples/assumenosideeffects6/CheckDiscarded.java b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
new file mode 100644
index 0000000..a249a6d
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// 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 assumenosideeffects6;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules-discard.txt b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
new file mode 100644
index 0000000..0f51575
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+ public static java.lang.Object methodStaticNull() return null;
+ public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class ** {
+ @assumenosideeffects6.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules.txt b/src/test/examples/assumenosideeffects6/keep-rules.txt
new file mode 100644
index 0000000..c158dd6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules.txt
@@ -0,0 +1,18 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+ public static java.lang.Object methodStaticNull() return null;
+ public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues7/Assumevalues.java b/src/test/examples/assumevalues7/Assumevalues.java
new file mode 100644
index 0000000..e83fc2e
--- /dev/null
+++ b/src/test/examples/assumevalues7/Assumevalues.java
@@ -0,0 +1,30 @@
+// 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 assumevalues7;
+
+class A {
+ public static Object getObjectStatic() {
+ return new Object();
+ }
+
+ // This method must be on a class which is not explicitly kept (as Assumevalues is).
+ // For an explicitly kept class we cannot expect single target for any methods, so the rule
+ // will never apply (see b/70550443#comment2).
+ public Object getObject() {
+ return new Object();
+ }
+}
+
+public class Assumevalues {
+ public static void main(String[] args) {
+ if (A.getObjectStatic() != null) {
+ System.out.println("NOPE_STATIC_NOT_NULL");
+ }
+ if (new A().getObject() != null) {
+ System.out.println("NOPE_NOT_NULL");
+ }
+ System.out.println("OK");
+ }
+}
diff --git a/src/test/examples/assumevalues7/keep-rules.txt b/src/test/examples/assumevalues7/keep-rules.txt
new file mode 100644
index 0000000..c8c6c2b
--- /dev/null
+++ b/src/test/examples/assumevalues7/keep-rules.txt
@@ -0,0 +1,15 @@
+# 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.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep class assumevalues7.Assumevalues {
+ void main(...);
+}
+
+# Mark some methods returning null.
+-assumevalues class assumevalues7.A {
+ public static java.lang.Object getObjectStatic() return null;
+ public java.lang.Object getObject() return null;
+}
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 d78f94b..37515d2 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1488,8 +1488,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/IdenticalCatchHandlerTest.java b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
index 450e6d0..55ec4f1 100644
--- a/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
+++ b/src/test/java/com/android/tools/r8/cf/IdenticalCatchHandlerTest.java
@@ -63,7 +63,7 @@
}
private int countCatchHandlers(AndroidApp inputApp) throws Exception {
- CodeInspector inspector = new CodeInspector(inputApp, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(inputApp);
DexClass dexClass = inspector.clazz(TestClass.class).getDexClass();
Code code = dexClass.virtualMethods()[0].getCode();
if (code.isCfCode()) {
diff --git a/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
index 7db1d61..632d0b9 100644
--- a/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/LambdaTestRunner.java
@@ -69,7 +69,7 @@
private static CfInvokeDynamic findFirstInMethod(AndroidApp app) throws Exception {
String returnType = "void";
- CodeInspector inspector = new CodeInspector(app, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(app);
List<String> args = Collections.singletonList(String[].class.getTypeName());
DexEncodedMethod method = inspector.clazz(CLASS).method(returnType, METHOD, args).getMethod();
CfCode code = method.getCode().asCfCode();
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/SynchronizedNoopTestRunner.java b/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
index e4f9249..84ac87c 100644
--- a/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/SynchronizedNoopTestRunner.java
@@ -11,18 +11,16 @@
import com.android.tools.r8.R8;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfMonitor;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.JarCode;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidAppConsumers;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import org.junit.Test;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.tree.AbstractInsnNode;
-import org.objectweb.asm.tree.InsnList;
-import org.objectweb.asm.tree.MethodNode;
public class SynchronizedNoopTestRunner {
private byte[] data;
@@ -40,21 +38,9 @@
CodeInspector inspector = new CodeInspector(a.build());
DexEncodedMethod method =
inspector.clazz(CLASS).method("void", "noop", Collections.emptyList()).getMethod();
- ArrayList<AbstractInsnNode> insns = new ArrayList<>();
- JarCode jarCode = method.getCode().asJarCode();
- MethodNode node = jarCode.getNode();
- assert node != null;
- InsnList asmInsns = node.instructions;
- for (int i = 0; i < asmInsns.size(); i++) {
- insns.add(asmInsns.get(i));
- }
- boolean hasMonitor =
- insns
- .stream()
- .anyMatch(
- insn ->
- insn.getOpcode() == Opcodes.MONITORENTER
- || insn.getOpcode() == Opcodes.MONITOREXIT);
+ CfCode cfCode = method.getCode().asCfCode();
+ List<CfInstruction> insns = cfCode.getInstructions();
+ boolean hasMonitor = insns.stream().anyMatch(insn -> insn instanceof CfMonitor);
assertFalse(hasMonitor);
}
}
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/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 6322840..2a56ef5 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -866,8 +866,8 @@
MethodSubject method = clazz.method("void", "main", ImmutableList.of("java.lang.String[]"));
assertThat(method, isPresent());
assertThat(
- method.getMethod().getCode().asJarCode().toString(),
- containsString("INVOKEINTERFACE classmerging/MergeDefaultMethodIntoClassTest$A.f"));
+ method.getMethod().getCode().asCfCode().toString(),
+ containsString("invokeinterface classmerging.MergeDefaultMethodIntoClassTest$A.f()V"));
runTestOnInput(main, app, preservedClassNames::contains, getProguardConfig(JAVA8_EXAMPLE_KEEP));
}
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/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index e727904..d5b88cb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -62,7 +62,7 @@
@Test
public void testWriteOnlyField_putObject_gone() throws Exception {
List<Path> processedApp = runR8(EXAMPLE_KEEP);
- CodeInspector inspector = new CodeInspector(processedApp, null, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(processedApp);
ClassSubject clazz = inspector.clazz(QUALIFIED_CLASS_NAME);
clazz.forAllMethods(
methodSubject -> {
@@ -77,7 +77,7 @@
@Test
public void testWriteOnlyField_dontoptimize() throws Exception {
List<Path> processedApp = runR8(DONT_OPTIMIZE);
- CodeInspector inspector = new CodeInspector(processedApp, null, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(processedApp);
ClassSubject clazz = inspector.clazz(QUALIFIED_CLASS_NAME);
assert backend == Backend.DEX || backend == Backend.CF;
clazz.forAllMethods(
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/memberrebinding/CompositionalLenseTest.java b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
index 4903304..517bd03 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/CompositionalLenseTest.java
@@ -93,7 +93,7 @@
options.enableInlining = false;
options.enableClassMerging = false;
});
- CodeInspector codeInspector = new CodeInspector(processedApp, o -> o.enableCfFrontend = true);
+ CodeInspector codeInspector = new CodeInspector(processedApp);
ClassSubject classSubject = codeInspector.clazz(TestMain.class);
assertThat(classSubject, isPresent());
MethodSubject methodSubject = classSubject.method(CodeInspector.MAIN);
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index bf63d70..6fccb9a 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -388,7 +388,7 @@
originalInspection.accept(inspector);
}
- CodeInspector inspector = new CodeInspector(processed, null, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(processed);
inspection.accept(inspector);
// We don't run Art, as the test R8RunExamplesTest already does that.
diff --git a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
index 6dd02c6..a66c222 100644
--- a/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
+++ b/src/test/java/com/android/tools/r8/movestringconstants/MoveStringConstantsTest.java
@@ -62,12 +62,7 @@
// setting (5) is just too small.
options.inliningInstructionLimit = 10;
});
- inspection.accept(
- new CodeInspector(
- app,
- options -> {
- options.enableCfFrontend = true;
- }));
+ inspection.accept(new CodeInspector(app));
if (backend == Backend.DEX) {
// Run on Art to check generated code against verifier.
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index aaecd58..ee25918 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -213,11 +213,7 @@
private static CodeInspector createDexInspector(AndroidApp outputApp)
throws IOException, ExecutionException {
- return new CodeInspector(
- outputApp,
- o -> {
- o.enableCfFrontend = true;
- });
+ return new CodeInspector(outputApp);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index 73cdaaf..ba7d491 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -17,8 +17,8 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
@@ -89,12 +89,7 @@
@Test
public void identiferMinifierTest() throws Exception {
- CodeInspector codeInspector =
- new CodeInspector(
- processedApp,
- options -> {
- options.enableCfFrontend = true;
- });
+ CodeInspector codeInspector = new CodeInspector(processedApp);
inspection.accept(codeInspector);
}
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 56dce3a..7c25445 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;
@@ -39,7 +40,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/neverreturnsnormally/NeverReturnsNormallyTest.java b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
index f401f6a..e6589d5a 100644
--- a/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
+++ b/src/test/java/com/android/tools/r8/neverreturnsnormally/NeverReturnsNormallyTest.java
@@ -72,13 +72,7 @@
Origin.unknown());
AndroidApp app =
ToolHelper.runR8(builder.build(), opts -> opts.enableClassInlining = enableClassInliner);
- inspection.accept(
- new CodeInspector(
- app,
- options -> {
- options.enableCfFrontend = true;
- }),
- mode);
+ inspection.accept(new CodeInspector(app), mode);
if (backend == Backend.DEX) {
// Run on Art to check generated code against verifier.
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/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index 911ceb7..a09b9a4 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -59,7 +59,7 @@
.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
}
AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
- CodeInspector inspector = new CodeInspector(app, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(app);
List<FoundClassSubject> classes = inspector.allClasses();
// Check that the synthetic class is still present.
@@ -103,7 +103,7 @@
.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
}
AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
- CodeInspector inspector = new CodeInspector(app, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(app);
List<FoundClassSubject> classes = inspector.allClasses();
// Check that the synthetic class is still present.
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/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index ee99b38..bfc8154 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -61,7 +61,7 @@
.setProgramConsumer(emptyConsumer(backend))
.build();
AndroidApp result = ToolHelper.runR8(command);
- CodeInspector inspector = new CodeInspector(result, o -> o.enableCfFrontend = true);
+ CodeInspector inspector = new CodeInspector(result);
Assert.assertFalse(inspector.clazz(SWITCHMAP_CLASS_NAME).isPresent());
}
}
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/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f79e01f..a690ee8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -417,12 +417,14 @@
assertTrue(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
matches |= 1 << 0;
} else if (rule.getName().matches("returns1")) {
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertTrue(rule.getReturnValue().isSingleValue());
assertEquals(1, rule.getReturnValue().getValueRange().getMin());
assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -432,6 +434,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(2, rule.getReturnValue().getValueRange().getMin());
assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -440,6 +443,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(234, rule.getReturnValue().getValueRange().getMin());
assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -448,15 +452,23 @@
assertFalse(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertTrue(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
assertEquals("int", rule.getReturnValue().getField().type.toString());
assertEquals("X", rule.getReturnValue().getField().name.toString());
matches |= 1 << 4;
+ } else if (rule.getName().matches("returnsNull")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isNull());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ matches |= 1 << 5;
} else {
fail("Unexpected");
}
}
- assertEquals((1 << 5) - 1, matches);
+ assertEquals((1 << 6) - 1, matches);
}
@Test
@@ -474,12 +486,14 @@
assertTrue(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
matches |= 1 << 0;
} else if (rule.getName().matches("is1")) {
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertTrue(rule.getReturnValue().isSingleValue());
assertEquals(1, rule.getReturnValue().getValueRange().getMin());
assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -489,6 +503,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(2, rule.getReturnValue().getValueRange().getMin());
assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -497,6 +512,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(234, rule.getReturnValue().getValueRange().getMin());
assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -505,15 +521,23 @@
assertFalse(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertTrue(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
assertEquals("int", rule.getReturnValue().getField().type.toString());
assertEquals("X", rule.getReturnValue().getField().name.toString());
matches |= 1 << 4;
+ } else if (rule.getName().matches("isNull")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isNull());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ matches |= 1 << 5;
} else {
fail("Unexpected");
}
}
- assertEquals((1 << 5) - 1, matches);
+ assertEquals((1 << 6) - 1, matches);
}
@Test
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/shaking/examples/TreeShakingAssumenosideeffects6Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
new file mode 100644
index 0000000..578ad2a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
@@ -0,0 +1,66 @@
+// 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumenosideeffects6Test extends TreeShakingTest {
+
+ @Parameters(name = "mode:{0}-{1} minify:{2}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (MinifyMode minify : MinifyMode.values()) {
+ parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+ parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+ parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+ }
+ return parameters;
+ }
+
+ public TreeShakingAssumenosideeffects6Test(
+ Frontend frontend, Backend backend, MinifyMode minify) {
+ super(
+ "examples/assumenosideeffects6",
+ "assumenosideeffects6.Assumenosideeffects",
+ frontend,
+ backend,
+ minify);
+ }
+
+ @Test
+ public void testKeeprules() throws Exception {
+ runTest(
+ null,
+ TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules.txt"));
+ }
+
+ @Test
+ public void testKeeprulesdiscard() throws Exception {
+ runTest(
+ null,
+ TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules-discard.txt"));
+ }
+
+ private static void assumenosideeffects6CheckOutput(String output1, String output2) {
+ Assert.assertEquals(
+ StringUtils.lines("methodStaticNull", "NOT NULL", "methodNull", "NOT NULL"), output1);
+ Assert.assertEquals(StringUtils.lines("NULL", "NULL"), output2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
new file mode 100644
index 0000000..789e40c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
@@ -0,0 +1,68 @@
+// 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.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumevalues7Test extends TreeShakingTest {
+
+ @Parameters(name = "mode:{0}-{1} minify:{2}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (MinifyMode minify : MinifyMode.values()) {
+ parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+ parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+ parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+ }
+ return parameters;
+ }
+
+ public TreeShakingAssumevalues7Test(Frontend frontend, Backend backend, MinifyMode minify) {
+ super("examples/assumevalues7", "assumevalues7.Assumevalues", frontend, backend, minify);
+ }
+
+ @Test
+ public void test() throws Exception {
+ runTest(
+ getBackend() == Backend.DEX ? TreeShakingAssumevalues7Test::assumevalues7CheckCode : null,
+ TreeShakingAssumevalues7Test::assumevalues7CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumevalues7/keep-rules.txt"));
+ }
+
+ private static void assumevalues7CheckCode(CodeInspector inspector) {
+ inspector.forAllClasses(c -> {
+ c.forAllMethods(m -> {
+ if (m.getFinalName().equals("main")) {
+ m.iterateInstructions().forEachRemaining(i -> {
+ if (i.isConstString(JumboStringMode.ALLOW)) {
+ ConstStringInstructionSubject str = (ConstStringInstructionSubject) i;
+ assert !str.getString().toASCIIString().contains("NOPE");
+ }
+ });
+ }
+ });
+ });
+ }
+
+ private static void assumevalues7CheckOutput(String output1, String output2) {
+ Assert.assertEquals(StringUtils.lines("NOPE_STATIC_NOT_NULL", "NOPE_NOT_NULL", "OK"), output1);
+ Assert.assertEquals(StringUtils.lines("OK"), output2);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
index b3e6367..004acc1 100644
--- a/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/proxy/ProxiesTest.java
@@ -78,7 +78,7 @@
.addLibraryFiles(ToolHelper.getJava8RuntimeJar());
}
AndroidApp app = ToolHelper.runR8(builder.build(), o -> o.enableDevirtualization = false);
- inspection.accept(new CodeInspector(app, o -> o.enableCfFrontend = true));
+ inspection.accept(new CodeInspector(app));
String result = backend == Backend.DEX ? runOnArt(app, mainClass) : runOnJava(app, mainClass);
if (ToolHelper.isWindows()) {
result = result.replace(System.lineSeparator(), "\n");
diff --git a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
index d12f41a..f37d6c6 100644
--- a/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/testrules/ForceInlineTest.java
@@ -61,7 +61,7 @@
.addLibraryFiles(library);
ToolHelper.allowTestProguardOptions(builder);
builder.addProguardConfiguration(proguardConfiguration, Origin.unknown());
- return new CodeInspector(ToolHelper.runR8(builder.build(), o -> o.enableCfFrontend = true));
+ return new CodeInspector(ToolHelper.runR8(builder.build()));
}
@Test
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
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 6fbbb20..bc31615 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -79,6 +79,7 @@
}
Timing timing = new Timing("CodeInspector");
InternalOptions options = new InternalOptions();
+ options.enableCfFrontend = true;
if (optionsConsumer != null) {
optionsConsumer.accept(options);
}
@@ -89,7 +90,7 @@
public CodeInspector(AndroidApp app) throws IOException, ExecutionException {
this(
- new ApplicationReader(app, new InternalOptions(), new Timing("CodeInspector"))
+ new ApplicationReader(app, runOptionsConsumer(null), new Timing("CodeInspector"))
.read(app.getProguardMapOutputData()));
}
@@ -102,13 +103,16 @@
private static InternalOptions runOptionsConsumer(Consumer<InternalOptions> optionsConsumer) {
InternalOptions internalOptions = new InternalOptions();
- optionsConsumer.accept(internalOptions);
+ internalOptions.enableCfFrontend = true;
+ if (optionsConsumer != null) {
+ optionsConsumer.accept(internalOptions);
+ }
return internalOptions;
}
public CodeInspector(AndroidApp app, Path proguardMap) throws IOException, ExecutionException {
this(
- new ApplicationReader(app, new InternalOptions(), new Timing("CodeInspector"))
+ new ApplicationReader(app, runOptionsConsumer(null), new Timing("CodeInspector"))
.read(StringResource.fromFile(proguardMap)));
}
diff --git a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
index 78d3683..b233ea3 100644
--- a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
+++ b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
@@ -9,4 +9,5 @@
public static int returns2To4() return 2..4;
public static int returns234To567() return 234..567;
public static int returnsField() return com.google.C.X;
+ public static Object returnsNull() return null;
}
diff --git a/src/test/proguard/valid/assume-values-with-return-value.flags b/src/test/proguard/valid/assume-values-with-return-value.flags
index 7f89a79..90cefbe 100644
--- a/src/test/proguard/valid/assume-values-with-return-value.flags
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -9,4 +9,5 @@
public static final int is2To4() return 2..4;
public static final int is234To567() return 234..567;
public static final int isField() return com.google.C.X;
+ public static final Object isNull() return null;
}
\ No newline at end of file