Version 1.6.44
Cherry pick: Allow R8 to dump all of the input to a zipfile
CL: https://r8-review.googlesource.com/c/r8/+/44880
Cherry pick: Fix dumping of input files
CL: https://r8-review.googlesource.com/c/r8/+/44888
Cherry pick: Dump all input when dumpInputToFile is set
CL: https://r8-review.googlesource.com/c/r8/+/45006
Change-Id: I62eb307daa1f0896a860aa4b806e84fad14a55f7
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index e124571..18f68ba 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -3,14 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-
import com.android.tools.r8.utils.ArchiveBuilder;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.DirectoryBuilder;
import com.android.tools.r8.utils.OutputBuilder;
import com.android.tools.r8.utils.ZipUtils;
-import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import java.io.IOException;
import java.nio.file.Files;
@@ -19,7 +16,6 @@
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.Set;
-import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
@@ -122,7 +118,7 @@
@Override
public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
- outputBuilder.addFile(getClassFileName(descriptor), data, handler);
+ outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
}
@Override
@@ -146,11 +142,6 @@
return outputBuilder.getPath();
}
- private static String getClassFileName(String classDescriptor) {
- assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
- return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + CLASS_EXTENSION;
- }
-
public static void writeResources(
Path archive, List<ProgramResource> resources, Set<DataEntryResource> dataResources)
throws IOException, ResourceException {
@@ -158,21 +149,11 @@
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
try (Closer closer = Closer.create()) {
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
- for (ProgramResource resource : resources) {
- assert resource.getClassDescriptors().size() == 1;
- String className = resource.getClassDescriptors().iterator().next();
- String entryName = getClassFileName(className);
- byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
- ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
- }
- for (DataEntryResource dataResource : dataResources) {
- String entryName = dataResource.getName();
- byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
- ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
- }
+ ZipUtils.writeResourcesToZip(resources, dataResources, closer, out);
}
}
}
+
}
/** Directory consumer to write program resources to a directory. */
@@ -208,7 +189,7 @@
@Override
public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
super.accept(data, descriptor, handler);
- outputBuilder.addFile(ArchiveConsumer.getClassFileName(descriptor), data, handler);
+ outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index ff5752e..1315918 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.6.43";
+ public static final String LABEL = "1.6.44";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index ae946fc..6e95cac 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -7,14 +7,17 @@
import static com.android.tools.r8.graph.ClassKind.LIBRARY;
import static com.android.tools.r8.graph.ClassKind.PROGRAM;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.ResourceException;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.Version;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.ClassKind;
import com.android.tools.r8.graph.DexApplication;
@@ -40,18 +43,31 @@
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Queue;
+import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import org.objectweb.asm.ClassVisitor;
public class ApplicationReader {
@@ -106,6 +122,10 @@
ProgramClassConflictResolver resolver)
throws IOException, ExecutionException {
assert verifyMainDexOptionsCompatible(inputApp, options);
+ if (options.dumpInputToFile != null) {
+ dumpInputToFile();
+ throw options.reporter.fatalError("Dumped compilation inputs to: " + options.dumpInputToFile);
+ }
timing.begin("DexApplication.read");
final LazyLoadedDexApplication.Builder builder =
DexApplication.builder(options, timing, resolver);
@@ -140,6 +160,143 @@
return builder.build();
}
+ private void dumpInputToFile() throws IOException {
+ try {
+ List<ProgramResourceProvider> programResourceProviders =
+ inputApp.getProgramResourceProviders();
+ Set<DataEntryResource> dataEntryResources = inputApp.getDataEntryResourcesForTesting();
+ List<ProgramResource> programResourcesWithDescriptors = new ArrayList<>();
+ for (ProgramResourceProvider programResourceProvider : programResourceProviders) {
+ addProgramResourcesWithDescriptor(
+ programResourcesWithDescriptors, programResourceProvider.getProgramResources());
+ }
+
+ List<ProgramResource> libraryProgramResourcesWithDescriptors =
+ getProgramResourcesWithDescriptors(inputApp.getLibraryResourceProviders());
+
+ List<ProgramResource> classpathProgramResourcesWithDescriptors =
+ getProgramResourcesWithDescriptors(inputApp.getClasspathResourceProviders());
+
+ OpenOption[] openOptions =
+ new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+ try (Closer closer = Closer.create()) {
+ try (ZipOutputStream out =
+ new ZipOutputStream(
+ Files.newOutputStream(Paths.get(options.dumpInputToFile), openOptions))) {
+ writeToZip(
+ dataEntryResources, programResourcesWithDescriptors, closer, out, "program.jar");
+ writeToZip(
+ ImmutableSet.of(),
+ libraryProgramResourcesWithDescriptors,
+ closer,
+ out,
+ "library.jar");
+ writeToZip(
+ ImmutableSet.of(),
+ classpathProgramResourcesWithDescriptors,
+ closer,
+ out,
+ "classpath.jar");
+ if (options.hasProguardConfiguration()) {
+ String proguardConfig = options.getProguardConfiguration().getParsedConfiguration();
+ ZipUtils.writeToZipStream(
+ out, "proguard.config", proguardConfig.getBytes(), ZipEntry.DEFLATED);
+ }
+
+ ZipUtils.writeToZipStream(
+ out, "r8-version", Version.getVersionString().getBytes(), ZipEntry.DEFLATED);
+ }
+ }
+ } catch (ResourceException e) {
+ options.reporter.fatalError("Failed to write input:" + e.getMessage());
+ }
+ }
+
+ private static void writeToZip(
+ Set<DataEntryResource> dataEntryResources,
+ List<ProgramResource> programResourcesWithDescriptors,
+ Closer closer,
+ ZipOutputStream out,
+ String entry)
+ throws IOException, ResourceException {
+ try (ByteArrayOutputStream programByteStream = new ByteArrayOutputStream()) {
+ try (ZipOutputStream archiveOutputStream = new ZipOutputStream(programByteStream)) {
+ ZipUtils.writeResourcesToZip(
+ programResourcesWithDescriptors, dataEntryResources, closer, archiveOutputStream);
+ }
+ ZipUtils.writeToZipStream(out, entry, programByteStream.toByteArray(), ZipEntry.DEFLATED);
+ }
+ }
+
+ private static List<ProgramResource> getProgramResourcesWithDescriptors(
+ List<ClassFileResourceProvider> classFileResourceProviders)
+ throws IOException, ResourceException {
+ ArrayList<ProgramResource> programResourcesWithDescriptors = new ArrayList<>();
+ for (ClassFileResourceProvider libraryResourceProvider : classFileResourceProviders) {
+ List<ProgramResource> programResources = new ArrayList<>();
+ for (String classDescriptor : libraryResourceProvider.getClassDescriptors()) {
+ programResources.add(libraryResourceProvider.getProgramResource(classDescriptor));
+ }
+ addProgramResourcesWithDescriptor(programResourcesWithDescriptors, programResources);
+ }
+ return programResourcesWithDescriptors;
+ }
+
+ private static void addProgramResourcesWithDescriptor(
+ List<ProgramResource> programResourcesWithDescriptors,
+ Collection<ProgramResource> programResources)
+ throws IOException, ResourceException {
+ for (ProgramResource programResource : programResources) {
+ if (programResource.getKind() != Kind.CF) {
+ continue;
+ }
+ try (InputStream inputStream = programResource.getByteStream()) {
+ byte[] bytes = ByteStreams.toByteArray(inputStream);
+ String descriptor = extractClassInternalType(bytes);
+ programResourcesWithDescriptors.add(
+ ProgramResource.fromBytes(
+ programResource.getOrigin(),
+ programResource.getKind(),
+ bytes,
+ ImmutableSet.of(descriptor)));
+ }
+ }
+ }
+
+ private static String extractClassInternalType(byte[] bytes) throws IOException {
+ class ClassNameExtractor extends ClassVisitor {
+ private String className;
+
+ private ClassNameExtractor() {
+ super(ASM_VERSION);
+ }
+
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ className = name;
+ }
+
+ String getDescriptor() {
+ return "L" + className + ";";
+ }
+ }
+
+ org.objectweb.asm.ClassReader reader = new org.objectweb.asm.ClassReader(bytes);
+ ClassNameExtractor extractor = new ClassNameExtractor();
+ reader.accept(
+ extractor,
+ org.objectweb.asm.ClassReader.SKIP_CODE
+ | org.objectweb.asm.ClassReader.SKIP_DEBUG
+ | org.objectweb.asm.ClassReader.SKIP_FRAMES);
+ return extractor.getDescriptor();
+ }
+
private static boolean verifyMainDexOptionsCompatible(
AndroidApp inputApp, InternalOptions options) {
if (!options.isGeneratingDex()) {
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index ba9af9d..af056a8 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -444,4 +444,9 @@
assert isValidJavaType(typeName);
return typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ".class";
}
+
+ public static String getClassFileName(String classDescriptor) {
+ assert classDescriptor != null && isClassDescriptor(classDescriptor);
+ return getClassBinaryNameFromDescriptor(classDescriptor) + CLASS_EXTENSION;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index faf5a9e..8e93b77 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -66,7 +66,6 @@
// Set to true to run compilation in a single thread and without randomly shuffling the input.
// This makes life easier when running R8 in a debugger.
public static final boolean DETERMINISTIC_DEBUGGING = false;
-
public enum LineNumberOptimization {
OFF,
ON
@@ -174,6 +173,8 @@
// To print memory one also have to enable printtimes.
public boolean printMemory = System.getProperty("com.android.tools.r8.printmemory") != null;
+ public String dumpInputToFile = System.getProperty("com.android.tools.r8.dumpinputtofile");
+
// Flag to toggle if DEX code objects should pass-through without IR processing.
public boolean passthroughDexCode = false;
// TODO(b/134705306): Currently allow merging dex files resulting from Java 8 library
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 da87e2b..89ef8f3 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -8,8 +8,12 @@
import static com.android.tools.r8.utils.FileUtils.MODULE_INFO_CLASS;
import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ResourceException;
import com.android.tools.r8.errors.CompilationError;
import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -19,7 +23,9 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.function.Predicate;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
@@ -28,6 +34,27 @@
public class ZipUtils {
+ public static void writeResourcesToZip(
+ List<ProgramResource> resources,
+ Set<DataEntryResource> dataResources,
+ Closer closer,
+ ZipOutputStream out)
+ throws IOException, ResourceException {
+ for (ProgramResource resource : resources) {
+ assert resource.getClassDescriptors().size() == 1;
+ Iterator<String> iterator = resource.getClassDescriptors().iterator();
+ String className = iterator.next();
+ String entryName = DescriptorUtils.getClassFileName(className);
+ byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
+ writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+ }
+ for (DataEntryResource dataResource : dataResources) {
+ String entryName = dataResource.getName();
+ byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
+ writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+ }
+ }
+
public interface OnEntryHandler {
void onEntry(ZipEntry entry, InputStream input) throws IOException;
}
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index 0bbc7bc..553ff0e 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -9,13 +9,20 @@
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.ZipUtils.OnEntryHandler;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.zip.ZipEntry;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -173,6 +180,53 @@
}
@Test
+ public void testDumpInputs() throws IOException {
+ Path out = temp.newFile("dex.zip").toPath();
+ Path dump = temp.newFile("dump.zip").toPath();
+ ProcessResult r8 =
+ ToolHelper.forkR8WithJavaOptions(
+ Paths.get("."),
+ ImmutableList.of("-Dcom.android.tools.r8.dumpinputtofile=" + dump.toString()),
+ "--lib",
+ ToolHelper.getDefaultAndroidJar().toString(),
+ "--dex",
+ "--output",
+ out.toString(),
+ "--pg-conf",
+ PROGUARD_FLAGS.toString(),
+ "--pg-conf",
+ testFlags.toString(),
+ INPUT_JAR.toString());
+
+ List<ZipEntry> entries = new ArrayList<>();
+ ZipUtils.iter(
+ dump.toString(),
+ new OnEntryHandler() {
+ @Override
+ public void onEntry(ZipEntry entry, InputStream input) throws IOException {
+ entries.add(entry);
+ }
+ });
+ Assert.assertTrue(hasEntry(entries, "program.jar"));
+ Assert.assertTrue(hasEntry(entries, "library.jar"));
+ Assert.assertTrue(hasEntry(entries, "classpath.jar"));
+ Assert.assertTrue(hasEntry(entries, "proguard.config"));
+ Assert.assertTrue(hasEntry(entries, "r8-version"));
+ // When dumping the inputs we throw an error in the program to exit early.
+ Assert.assertNotEquals(0, r8.exitCode);
+ }
+
+ private boolean hasEntry(Collection<ZipEntry> entries, String name) {
+ for (ZipEntry entry : entries) {
+ if (entry.getName().equals(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Test
public void testSpecifyDexAndClassfileNotAllowed() throws IOException, InterruptedException {
Path out = temp.newFile("dex.zip").toPath();
ProcessResult r8 =
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 2819cc7..141e3fb 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1329,6 +1329,12 @@
return forkJava(dir, R8.class, args);
}
+ public static ProcessResult forkR8WithJavaOptions(
+ Path dir, List<String> javaOptions, String... args) throws IOException {
+ String r8Jar = R8_JAR.toAbsolutePath().toString();
+ return forkJavaWithJarAndJavaOptions(dir, r8Jar, Arrays.asList(args), javaOptions);
+ }
+
public static ProcessResult forkR8Jar(Path dir, String... args)
throws IOException, InterruptedException {
String r8Jar = R8_JAR.toAbsolutePath().toString();
@@ -1355,14 +1361,23 @@
private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
throws IOException {
- List<String> command = new ImmutableList.Builder<String>()
- .add(getJavaExecutable())
- .add("-jar").add(jarPath)
- .addAll(args)
- .build();
+ return forkJavaWithJarAndJavaOptions(dir, jarPath, args, ImmutableList.of());
+ }
+
+ private static ProcessResult forkJavaWithJarAndJavaOptions(
+ Path dir, String jarPath, List<String> args, List<String> javaOptions) throws IOException {
+ List<String> command =
+ new ImmutableList.Builder<String>()
+ .add(getJavaExecutable())
+ .addAll(javaOptions)
+ .add("-jar")
+ .add(jarPath)
+ .addAll(args)
+ .build();
return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
}
+
private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
throws IOException {
List<String> command = new ImmutableList.Builder<String>()