summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2017-03-16 18:57:48 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-03-16 18:57:48 +0000
commit19ccfc6886359d1df55cfc0f12095298a85bfeca (patch)
tree0c0263f40a2f6a94f5558e455a8e40630841a1ae
parentacbf2b01f2a43b24dfeb8209fc8e968b0b241de8 (diff)
parentaee44accbc04c0e80ef6936597123db4725f3c33 (diff)
downloaddesugar-19ccfc6886359d1df55cfc0f12095298a85bfeca.tar.gz
Merge remote-tracking branch 'aosp/upstream-master' into master am: a81405c6dd am: 20dfe722ed
am: aee44accbc Change-Id: I8eb48b6c6a77b3df1bf753f3d8ae3104e81bc643
-rw-r--r--java/com/google/devtools/build/android/desugar/Desugar.java231
-rw-r--r--java/com/google/devtools/build/android/desugar/LambdaClassMaker.java12
-rw-r--r--java/com/google/devtools/build/android/desugar/LambdaDesugaring.java2
3 files changed, 144 insertions, 101 deletions
diff --git a/java/com/google/devtools/build/android/desugar/Desugar.java b/java/com/google/devtools/build/android/desugar/Desugar.java
index 877d230..6f9e0f0 100644
--- a/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -16,6 +16,7 @@ package com.google.devtools.build.android.desugar;
import static com.google.common.base.Preconditions.checkState;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
@@ -34,6 +35,7 @@ import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Enumeration;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
@@ -54,13 +56,16 @@ class Desugar {
public static class Options extends OptionsBase {
@Option(
name = "input",
- defaultValue = "null",
+ allowMultiple = true,
+ defaultValue = "",
category = "input",
converter = ExistingPathConverter.class,
abbrev = 'i',
- help = "Input Jar with classes to desugar (required)."
+ help =
+ "Input Jar with classes to desugar (required, the n-th input is paired with the n-th "
+ + "output)."
)
- public Path inputJar;
+ public List<Path> inputJars;
@Option(
name = "classpath_entry",
@@ -92,13 +97,16 @@ class Desugar {
@Option(
name = "output",
- defaultValue = "null",
+ allowMultiple = true,
+ defaultValue = "",
category = "output",
converter = PathConverter.class,
abbrev = 'o',
- help = "Output Jar to write desugared classes into (required)."
+ help =
+ "Output Jar to write desugared classes into (required, the n-th output is paired with "
+ + "the n-th input)."
)
- public Path outputJar;
+ public List<Path> outputJars;
@Option(
name = "verbose",
@@ -157,10 +165,12 @@ class Desugar {
optionsParser.parseAndExitUponError(args);
Options options = optionsParser.getOptions(Options.class);
- checkState(options.inputJar != null, "--input is required");
- checkState(options.outputJar != null, "--output is required");
+ checkState(!options.inputJars.isEmpty(), "--input is required");
+ checkState(
+ options.inputJars.size() == options.outputJars.size(),
+ "Desugar requires the same number of inputs and outputs to pair them");
checkState(!options.bootclasspath.isEmpty() || options.allowEmptyBootclasspath,
- "Bootclasspath required to desugar %s", options.inputJar);
+ "At least one --bootclasspath_entry is required");
if (options.verbose) {
System.out.printf("Lambda classes will be written under %s%n", dumpDirectory);
@@ -169,109 +179,127 @@ class Desugar {
CoreLibraryRewriter rewriter =
new CoreLibraryRewriter(options.coreLibrary ? "__desugar__/" : "");
- IndexedJars appIndexedJar = new IndexedJars(ImmutableList.of(options.inputJar));
- IndexedJars appAndClasspathIndexedJars = new IndexedJars(options.classpath, appIndexedJar);
- ClassLoader loader =
- createClassLoader(rewriter, options.bootclasspath, appAndClasspathIndexedJars);
boolean allowDefaultMethods = options.minSdkVersion >= 24;
boolean allowCallsToObjectsNonNull = options.minSdkVersion >= 19;
- try (ZipFile in = new ZipFile(options.inputJar.toFile());
- ZipOutputStream out =
- new ZipOutputStream(
- new BufferedOutputStream(Files.newOutputStream(options.outputJar)))) {
- LambdaClassMaker lambdas = new LambdaClassMaker(dumpDirectory);
- ClassReaderFactory readerFactory =
- new ClassReaderFactory(
- (options.copyBridgesFromClasspath && !allowDefaultMethods)
- ? appAndClasspathIndexedJars
- : appIndexedJar,
- rewriter);
-
- ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
-
- // Process input Jar, desugaring as we go
- for (Enumeration<? extends ZipEntry> entries = in.entries(); entries.hasMoreElements(); ) {
- ZipEntry entry = entries.nextElement();
- try (InputStream content = in.getInputStream(entry)) {
- // We can write classes uncompressed since they need to be converted to .dex format for
- // Android anyways. Resources are written as they were in the input jar to avoid any
- // danger of accidentally uncompressed resources ending up in an .apk.
- if (entry.getName().endsWith(".class")) {
- ClassReader reader = rewriter.reader(content);
+
+ LambdaClassMaker lambdas = new LambdaClassMaker(dumpDirectory);
+
+ // Process each input separately
+ for (InputOutputPair inputOutputPair : toInputOutputPairs(options)) {
+ Path inputJar = inputOutputPair.getInput();
+ IndexedJars appIndexedJar = new IndexedJars(ImmutableList.of(inputJar));
+ IndexedJars appAndClasspathIndexedJars = new IndexedJars(options.classpath, appIndexedJar);
+ ClassLoader loader =
+ createClassLoader(rewriter, options.bootclasspath, appAndClasspathIndexedJars);
+
+ try (ZipFile in = new ZipFile(inputJar.toFile());
+ ZipOutputStream out =
+ new ZipOutputStream(
+ new BufferedOutputStream(Files.newOutputStream(inputOutputPair.getOutput())))) {
+ ClassReaderFactory readerFactory =
+ new ClassReaderFactory(
+ (options.copyBridgesFromClasspath && !allowDefaultMethods)
+ ? appAndClasspathIndexedJars
+ : appIndexedJar,
+ rewriter);
+
+ ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
+
+ // Process input Jar, desugaring as we go
+ for (Enumeration<? extends ZipEntry> entries = in.entries(); entries.hasMoreElements(); ) {
+ ZipEntry entry = entries.nextElement();
+ try (InputStream content = in.getInputStream(entry)) {
+ // We can write classes uncompressed since they need to be converted to .dex format for
+ // Android anyways. Resources are written as they were in the input jar to avoid any
+ // danger of accidentally uncompressed resources ending up in an .apk.
+ if (entry.getName().endsWith(".class")) {
+ ClassReader reader = rewriter.reader(content);
+ CoreLibraryRewriter.UnprefixingClassWriter writer =
+ rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
+ ClassVisitor visitor = writer;
+ if (!allowDefaultMethods) {
+ visitor = new Java7Compatibility(visitor, readerFactory);
+ }
+
+ visitor =
+ new LambdaDesugaring(
+ visitor, loader, lambdas, interfaceLambdaMethodCollector,
+ allowDefaultMethods);
+
+ if (!allowCallsToObjectsNonNull) {
+ visitor = new ObjectsRequireNonNullMethodInliner(visitor);
+ }
+ reader.accept(visitor, 0);
+
+ writeStoredEntry(out, entry.getName(), writer.toByteArray());
+ } else {
+ // TODO(bazel-team): Avoid de- and re-compressing resource files
+ ZipEntry destEntry = new ZipEntry(entry);
+ destEntry.setCompressedSize(-1);
+ out.putNextEntry(destEntry);
+ ByteStreams.copy(content, out);
+ out.closeEntry();
+ }
+ }
+ }
+
+ ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
+ if (allowDefaultMethods) {
+ checkState(
+ interfaceLambdaMethods.isEmpty(),
+ "Desugaring with default methods enabled moved interface lambdas");
+ }
+
+ // Write out the lambda classes we generated along the way
+ for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdas.drain().entrySet()) {
+ try (InputStream bytecode =
+ Files.newInputStream(dumpDirectory.resolve(lambdaClass.getKey()))) {
+ ClassReader reader = rewriter.reader(bytecode);
CoreLibraryRewriter.UnprefixingClassWriter writer =
- rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
+ rewriter.writer(ClassWriter.COMPUTE_MAXS /*for invoking bridges*/);
ClassVisitor visitor = writer;
+
if (!allowDefaultMethods) {
- visitor = new Java7Compatibility(visitor, readerFactory);
+ // null ClassReaderFactory b/c we don't expect to need it for lambda classes
+ visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null);
}
visitor =
- new LambdaDesugaring(
- visitor, loader, lambdas, interfaceLambdaMethodCollector, allowDefaultMethods);
-
+ new LambdaClassFixer(
+ visitor,
+ lambdaClass.getValue(),
+ readerFactory,
+ interfaceLambdaMethods,
+ allowDefaultMethods);
+ // Send lambda classes through desugaring to make sure there's no invokedynamic
+ // instructions in generated lambda classes (checkState below will fail)
+ visitor = new LambdaDesugaring(visitor, loader, lambdas, null, allowDefaultMethods);
if (!allowCallsToObjectsNonNull) {
+ // Not sure whether there will be implicit null check emitted by javac, so we rerun
+ // the inliner again
visitor = new ObjectsRequireNonNullMethodInliner(visitor);
}
reader.accept(visitor, 0);
-
- writeStoredEntry(out, entry.getName(), writer.toByteArray());
- } else {
- // TODO(bazel-team): Avoid de- and re-compressing resource files
- ZipEntry destEntry = new ZipEntry(entry);
- destEntry.setCompressedSize(-1);
- out.putNextEntry(destEntry);
- ByteStreams.copy(content, out);
- out.closeEntry();
+ String filename =
+ rewriter.unprefix(lambdaClass.getValue().desiredInternalName()) + ".class";
+ writeStoredEntry(out, filename, writer.toByteArray());
}
}
- }
- ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
- if (allowDefaultMethods) {
- checkState(
- interfaceLambdaMethods.isEmpty(),
- "Desugaring with default methods enabled moved interface lambdas");
- }
-
- // Write out the lambda classes we generated along the way
- for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdas.drain().entrySet()) {
- try (InputStream bytecode =
- Files.newInputStream(dumpDirectory.resolve(lambdaClass.getKey()))) {
- ClassReader reader = rewriter.reader(bytecode);
- CoreLibraryRewriter.UnprefixingClassWriter writer =
- rewriter.writer(ClassWriter.COMPUTE_MAXS /*for invoking bridges*/);
- ClassVisitor visitor = writer;
-
- if (!allowDefaultMethods) {
- // null ClassReaderFactory b/c we don't expect to need it for lambda classes
- visitor = new Java7Compatibility(visitor, (ClassReaderFactory) null);
- }
-
- visitor =
- new LambdaClassFixer(
- visitor,
- lambdaClass.getValue(),
- readerFactory,
- interfaceLambdaMethods,
- allowDefaultMethods);
- // Send lambda classes through desugaring to make sure there's no invokedynamic
- // instructions in generated lambda classes (checkState below will fail)
- visitor = new LambdaDesugaring(visitor, loader, lambdas, null, allowDefaultMethods);
- if (!allowCallsToObjectsNonNull) {
- // Not sure whether there will be implicit null check emitted by javac, so we rerun the
- // inliner again
- visitor = new ObjectsRequireNonNullMethodInliner(visitor);
- }
- reader.accept(visitor, 0);
- String filename =
- rewriter.unprefix(lambdaClass.getValue().desiredInternalName()) + ".class";
- writeStoredEntry(out, filename, writer.toByteArray());
- }
+ Map<Path, LambdaInfo> leftBehind = lambdas.drain();
+ checkState(leftBehind.isEmpty(), "Didn't process %s", leftBehind);
}
+ }
+ }
- Map<Path, LambdaInfo> leftBehind = lambdas.drain();
- checkState(leftBehind.isEmpty(), "Didn't process %s", leftBehind);
+ private static List<InputOutputPair> toInputOutputPairs(Options options) {
+ final ImmutableList.Builder<InputOutputPair> ioPairListbuilder = ImmutableList.builder();
+ for (Iterator<Path> inputIt = options.inputJars.iterator(),
+ outputIt = options.outputJars.iterator();
+ inputIt.hasNext();) {
+ ioPairListbuilder.add(InputOutputPair.create(inputIt.next(), outputIt.next()));
}
+ return ioPairListbuilder.build();
}
private static void writeStoredEntry(ZipOutputStream out, String filename, byte[] content)
@@ -357,4 +385,19 @@ class Desugar {
});
}
}
+
+ /**
+ * Pair input and output.
+ */
+ @AutoValue
+ abstract static class InputOutputPair {
+
+ static InputOutputPair create(Path input, Path output) {
+ return new AutoValue_Desugar_InputOutputPair(input, output);
+ }
+
+ abstract Path getInput();
+
+ abstract Path getOutput();
+ }
}
diff --git a/java/com/google/devtools/build/android/desugar/LambdaClassMaker.java b/java/com/google/devtools/build/android/desugar/LambdaClassMaker.java
index d8f2e28..2a9f9be 100644
--- a/java/com/google/devtools/build/android/desugar/LambdaClassMaker.java
+++ b/java/com/google/devtools/build/android/desugar/LambdaClassMaker.java
@@ -54,7 +54,7 @@ class LambdaClassMaker {
}
/**
- * Returns relative paths to .class files generated since the last call to this method together
+ * Returns absolute paths to .class files generated since the last call to this method together
* with a string descriptor of the factory method.
*/
public Map<Path, LambdaInfo> drain() {
@@ -68,12 +68,12 @@ class LambdaClassMaker {
// will not contain '/' and searches will fail. So, construct an absolute path from the given
// string and use its string representation to find the file we need regardless of host
// system's file system
- final String rootPathPrefixStr = rootDirectory.resolve(pathPrefix).toString();
+ Path rootPathPrefix = rootDirectory.resolve(pathPrefix);
+ final String rootPathPrefixStr = rootPathPrefix.toString();
- // TODO(kmb): Investigate making this faster in the case of many lambdas
// TODO(bazel-team): This could be much nicer with lambdas
- try (Stream<Path> results =
- Files.walk(rootDirectory)
+ try (Stream<Path> paths =
+ Files.list(rootPathPrefix.getParent())
.filter(
new Predicate<Path>() {
@Override
@@ -82,7 +82,7 @@ class LambdaClassMaker {
&& !generatedClasses.containsKey(path);
}
})) {
- return Iterators.getOnlyElement(results.iterator());
+ return Iterators.getOnlyElement(paths.iterator());
}
}
}
diff --git a/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java b/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
index c52c4a4..c808831 100644
--- a/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
+++ b/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java
@@ -220,7 +220,7 @@ class LambdaDesugaring extends ClassVisitor {
return result; // we're already queued up a bridge method for this method reference
}
- String name = "bridge$lambda$" + bridgeMethods.size();
+ String name = uniqueInPackage(internalName, "bridge$lambda$" + bridgeMethods.size());
Handle bridgeMethod;
switch (invokedMethod.getTag()) {
case Opcodes.H_INVOKESTATIC: