aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android/java/com/google/devtools
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-03-23 16:53:40 +0000
committerGravatar Yue Gan <yueg@google.com>2017-03-23 17:53:43 +0000
commite182500db1f331fd39f7e5d6215e6fdedbb0791a (patch)
tree675f0071d456925d93ccc4da68a42e772c3e7c77 /src/tools/android/java/com/google/devtools
parentfa27b5045df54394a07e22e247767e9ecca57b38 (diff)
Avoid to re-index classpath and bootclasspath
Avoid to re-index classpath and bootclasspath for each input/output pair since they share the same classpath and bootclasspath. RELNOTES: n/a -- PiperOrigin-RevId: 151015798 MOS_MIGRATED_REVID=151015798
Diffstat (limited to 'src/tools/android/java/com/google/devtools')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java260
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/IndexedInputs.java67
2 files changed, 174 insertions, 153 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
index 22780ea790..1711f57a99 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Desugar.java
@@ -26,6 +26,7 @@ import com.google.devtools.build.android.Converters.PathConverter;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
+import com.google.errorprone.annotations.MustBeClosed;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
@@ -193,124 +194,137 @@ class Desugar {
LambdaClassMaker lambdas = new LambdaClassMaker(dumpDirectory);
- // Process each input separately
- for (InputOutputPair inputOutputPair : toInputOutputPairs(options)) {
- Path inputPath = inputOutputPair.getInput();
- Path outputPath = inputOutputPair.getOutput();
- checkState(
- Files.isDirectory(inputPath) || !Files.isDirectory(outputPath),
- "Input jar file requires an output jar file");
-
- try (Closer closer = Closer.create();
- OutputFileProvider outputFileProvider = toOutputFileProvider(outputPath)) {
- InputFileProvider appInputFiles = toInputFileProvider(closer, inputPath);
- List<InputFileProvider> classpathInputFiles =
- toInputFileProvider(closer, options.classpath);
- IndexedInputs appIndexedInputs = new IndexedInputs(ImmutableList.of(appInputFiles));
- IndexedInputs appAndClasspathIndexedInputs =
- new IndexedInputs(classpathInputFiles, appIndexedInputs);
- ClassLoader loader =
- createClassLoader(
- rewriter,
- toInputFileProvider(closer, options.bootclasspath),
- appAndClasspathIndexedInputs);
-
- ClassReaderFactory readerFactory =
- new ClassReaderFactory(
- (options.copyBridgesFromClasspath && !allowDefaultMethods)
- ? appAndClasspathIndexedInputs
- : appIndexedInputs,
- rewriter);
-
- ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
-
- // Process inputs, desugaring as we go
- for (String filename : appInputFiles) {
- try (InputStream content = appInputFiles.getInputStream(filename)) {
- // 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 (filename.endsWith(".class")) {
- ClassReader reader = rewriter.reader(content);
- CoreLibraryRewriter.UnprefixingClassWriter writer =
- rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
- ClassVisitor visitor = writer;
-
- if (!options.onlyDesugarJavac9ForLint) {
- if (!allowDefaultMethods) {
- visitor = new Java7Compatibility(visitor, readerFactory);
+ try (Closer closer = Closer.create()) {
+ IndexedInputs indexedClasspath =
+ new IndexedInputs(toRegisteredInputFileProvider(closer, options.classpath));
+ // Use a classloader that as much as possible uses the provided bootclasspath instead of
+ // the tool's system classloader. Unfortunately we can't do that for java. classes.
+ ClassLoader bootclassloader =
+ options.bootclasspath.isEmpty()
+ ? new ThrowingClassLoader()
+ : new HeaderClassLoader(
+ new IndexedInputs(toRegisteredInputFileProvider(closer, options.bootclasspath)),
+ rewriter,
+ new ThrowingClassLoader());
+
+ // Process each input separately
+ for (InputOutputPair inputOutputPair : toInputOutputPairs(options)) {
+ Path inputPath = inputOutputPair.getInput();
+ Path outputPath = inputOutputPair.getOutput();
+ checkState(
+ Files.isDirectory(inputPath) || !Files.isDirectory(outputPath),
+ "Input jar file requires an output jar file");
+
+ try (OutputFileProvider outputFileProvider = toOutputFileProvider(outputPath);
+ InputFileProvider inputFiles = toInputFileProvider(inputPath)) {
+ IndexedInputs indexedInputFiles = new IndexedInputs(ImmutableList.of(inputFiles));
+ // Prepend classpath with input file itself so LambdaDesugaring can load classes with
+ // lambdas.
+ IndexedInputs indexedClasspathAndInputFiles =
+ indexedClasspath.withParent(indexedInputFiles);
+ // Note that input file and classpath need to be in the same classloader because
+ // we typically get the header Jar for inputJar on the classpath and having the header
+ // Jar in a parent loader means the header version is preferred over the real thing.
+ ClassLoader loader =
+ new HeaderClassLoader(indexedClasspathAndInputFiles, rewriter, bootclassloader);
+
+ ClassReaderFactory readerFactory =
+ new ClassReaderFactory(
+ (options.copyBridgesFromClasspath && !allowDefaultMethods)
+ ? indexedClasspathAndInputFiles
+ : indexedInputFiles,
+ rewriter);
+
+ ImmutableSet.Builder<String> interfaceLambdaMethodCollector = ImmutableSet.builder();
+
+ // Process inputs, desugaring as we go
+ for (String filename : inputFiles) {
+ try (InputStream content = inputFiles.getInputStream(filename)) {
+ // 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 (filename.endsWith(".class")) {
+ ClassReader reader = rewriter.reader(content);
+ CoreLibraryRewriter.UnprefixingClassWriter writer =
+ rewriter.writer(ClassWriter.COMPUTE_MAXS /*for bridge methods*/);
+ ClassVisitor visitor = writer;
+
+ if (!options.onlyDesugarJavac9ForLint) {
+ 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);
+
+ outputFileProvider.write(filename, writer.toByteArray());
+ } else {
+ outputFileProvider.copyFrom(filename, inputFiles);
+ }
+ }
+ }
- visitor =
- new LambdaDesugaring(
- visitor,
- loader,
- lambdas,
- interfaceLambdaMethodCollector,
- allowDefaultMethods);
+ ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
+ checkState(
+ !allowDefaultMethods || interfaceLambdaMethods.isEmpty(),
+ "Desugaring with default methods enabled moved interface lambdas");
+
+ // Write out the lambda classes we generated along the way
+ ImmutableMap<Path, LambdaInfo> lambdaClasses = lambdas.drain();
+ checkState(
+ !options.onlyDesugarJavac9ForLint || lambdaClasses.isEmpty(),
+ "There should be no lambda classes generated: %s",
+ lambdaClasses.keySet());
+
+ for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdaClasses.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";
outputFileProvider.write(filename, writer.toByteArray());
- } else {
- outputFileProvider.copyFrom(filename, appInputFiles);
}
}
- }
-
- ImmutableSet<String> interfaceLambdaMethods = interfaceLambdaMethodCollector.build();
- checkState(
- !allowDefaultMethods || interfaceLambdaMethods.isEmpty(),
- "Desugaring with default methods enabled moved interface lambdas");
-
- // Write out the lambda classes we generated along the way
- ImmutableMap<Path, LambdaInfo> lambdaClasses = lambdas.drain();
- checkState(
- !options.onlyDesugarJavac9ForLint || lambdaClasses.isEmpty(),
- "There should be no lambda classes generated: %s",
- lambdaClasses.keySet());
-
- for (Map.Entry<Path, LambdaInfo> lambdaClass : lambdaClasses.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";
- outputFileProvider.write(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);
}
}
}
@@ -325,24 +339,6 @@ class Desugar {
return ioPairListbuilder.build();
}
- private static ClassLoader createClassLoader(
- CoreLibraryRewriter rewriter,
- List<InputFileProvider> bootclasspath,
- IndexedInputs appAndClasspathIndexedInputs)
- throws IOException {
- // Use a classloader that as much as possible uses the provided bootclasspath instead of
- // the tool's system classloader. Unfortunately we can't do that for java. classes.
- ClassLoader parent = new ThrowingClassLoader();
- if (!bootclasspath.isEmpty()) {
- parent = new HeaderClassLoader(new IndexedInputs(bootclasspath), rewriter, parent);
- }
- // Prepend classpath with input jar itself so LambdaDesugaring can load classes with lambdas.
- // Note that inputJar and classpath need to be in the same classloader because we typically get
- // the header Jar for inputJar on the classpath and having the header Jar in a parent loader
- // means the header version is preferred over the real thing.
- return new HeaderClassLoader(appAndClasspathIndexedInputs, rewriter, parent);
- }
-
private static class ThrowingClassLoader extends ClassLoader {
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
@@ -394,6 +390,7 @@ class Desugar {
}
/** Transform a Path to an {@link OutputFileProvider} */
+ @MustBeClosed
private static OutputFileProvider toOutputFileProvider(Path path)
throws IOException {
if (Files.isDirectory(path)) {
@@ -403,17 +400,22 @@ class Desugar {
}
}
- /** Transform a Path to an InputFileProvider and register it to close it at the end of desugar */
- private static InputFileProvider toInputFileProvider(Closer closer, Path path)
+ /** Transform a Path to an InputFileProvider that needs to be closed by the caller. */
+ @MustBeClosed
+ private static InputFileProvider toInputFileProvider(Path path)
throws IOException {
if (Files.isDirectory(path)) {
- return closer.register(new DirectoryInputFileProvider(path));
+ return new DirectoryInputFileProvider(path);
} else {
- return closer.register(new ZipInputFileProvider(path));
+ return new ZipInputFileProvider(path);
}
}
- private static ImmutableList<InputFileProvider> toInputFileProvider(
+ /**
+ * Transform a list of Path to a list of ZipInputFileProvider and register them with the given
+ * closer.
+ */
+ private static ImmutableList<InputFileProvider> toRegisteredInputFileProvider(
Closer closer, List<Path> paths) throws IOException {
ImmutableList.Builder<InputFileProvider> builder = new ImmutableList.Builder<>();
for (Path path : paths) {
@@ -422,7 +424,7 @@ class Desugar {
}
return builder.build();
}
-
+
/**
* Pair input and output.
*/
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/IndexedInputs.java b/src/tools/android/java/com/google/devtools/build/android/desugar/IndexedInputs.java
index 58459ccbe5..33c6132020 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/IndexedInputs.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/IndexedInputs.java
@@ -13,11 +13,14 @@
// limitations under the License.
package com.google.devtools.build.android.desugar;
-import com.google.common.base.Preconditions;
-import java.io.IOException;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
/**
@@ -27,36 +30,47 @@ import javax.annotation.Nullable;
*/
class IndexedInputs {
- private final Map<String, InputFileProvider> inputFiles = new HashMap<>();
+ private final ImmutableMap<String, InputFileProvider> inputFiles;
- /** Parent indexed inputs to use before to search a file name into this indexed inputs. */
+ /**
+ * Parent {@link IndexedInputs} to use before to search a file name into this {@link
+ * IndexedInputs}.
+ */
@Nullable
- private final IndexedInputs parentIndexedInputs;
+ private final IndexedInputs parent;
- /** Index a list of input files without a parent indexed inputs. */
- public IndexedInputs(List<InputFileProvider> inputProviders) throws IOException {
- this(inputProviders, null);
+ /** Index a list of input files without a parent {@link IndexedInputs}. */
+ public IndexedInputs(List<InputFileProvider> inputProviders) {
+ this.parent = null;
+ this.inputFiles = indexInputs(inputProviders);
}
/**
- * Index a list of input files and set a parent indexed inputs that is firstly used during the
- * search of a file name.
+ * Create a new {@link IndexedInputs} with input files previously indexed and with a parent {@link
+ * IndexedInputs}.
*/
- public IndexedInputs(
- List<InputFileProvider> inputProviders, @Nullable IndexedInputs parentIndexedInputs)
- throws IOException {
- this.parentIndexedInputs = parentIndexedInputs;
- for (InputFileProvider inputProvider : inputProviders) {
- indexInput(inputProvider);
- }
+ private IndexedInputs(
+ ImmutableMap<String, InputFileProvider> inputFiles, IndexedInputs parentIndexedInputs) {
+ this.parent = parentIndexedInputs;
+ this.inputFiles = inputFiles;
+ }
+
+ /**
+ * Create a new {@link IndexedInputs} with input files already indexed and with a parent {@link
+ * IndexedInputs}.
+ */
+ @CheckReturnValue
+ public IndexedInputs withParent(IndexedInputs parent) {
+ checkState(this.parent == null);
+ return new IndexedInputs(this.inputFiles, parent);
}
@Nullable
public InputFileProvider getInputFileProvider(String filename) {
- Preconditions.checkArgument(filename.endsWith(".class"));
+ checkArgument(filename.endsWith(".class"));
- if (parentIndexedInputs != null) {
- InputFileProvider inputFileProvider = parentIndexedInputs.getInputFileProvider(filename);
+ if (parent != null) {
+ InputFileProvider inputFileProvider = parent.getInputFileProvider(filename);
if (inputFileProvider != null) {
return inputFileProvider;
}
@@ -65,11 +79,16 @@ class IndexedInputs {
return inputFiles.get(filename);
}
- private void indexInput(final InputFileProvider inputFileProvider) throws IOException {
- for (String relativePath : inputFileProvider) {
- if (relativePath.endsWith(".class") && !inputFiles.containsKey(relativePath)) {
- inputFiles.put(relativePath, inputFileProvider);
+ private ImmutableMap<String, InputFileProvider> indexInputs(
+ List<InputFileProvider> inputProviders) {
+ Map<String, InputFileProvider> indexedInputs = new HashMap<>();
+ for (InputFileProvider inputProvider : inputProviders) {
+ for (String relativePath : inputProvider) {
+ if (relativePath.endsWith(".class") && !indexedInputs.containsKey(relativePath)) {
+ indexedInputs.put(relativePath, inputProvider);
+ }
}
}
+ return ImmutableMap.copyOf(indexedInputs);
}
}