diff options
40 files changed, 354 insertions, 94 deletions
diff --git a/src/test/java/com/google/devtools/build/android/desugar/BUILD b/src/test/java/com/google/devtools/build/android/desugar/BUILD index 602a48e326..da56672a5c 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/BUILD +++ b/src/test/java/com/google/devtools/build/android/desugar/BUILD @@ -13,6 +13,7 @@ filegroup( srcs = glob(["**"]) + [ "//src/test/java/com/google/devtools/build/android/desugar/classes_for_testing_type_inference:srcs", "//src/test/java/com/google/devtools/build/android/desugar/dependencies:srcs", + "//src/test/java/com/google/devtools/build/android/desugar/io:srcs", "//src/test/java/com/google/devtools/build/android/desugar/runtime:srcs", ], visibility = ["//src/test/java/com/google/devtools/build/android:__pkg__"], @@ -367,6 +368,7 @@ java_test( deps = [ "//src/tools/android/java/com/google/devtools/build/android/desugar", "//src/tools/android/java/com/google/devtools/build/android/desugar:deps_collector_api", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//third_party:asm", "//third_party:asm-tree", "//third_party:guava", @@ -388,22 +390,12 @@ java_test( ) java_test( - name = "FieldInfoTest", - size = "small", - srcs = ["FieldInfoTest.java"], - deps = [ - "//src/tools/android/java/com/google/devtools/build/android/desugar", - "//third_party:junit4", - "//third_party:truth", - ], -) - -java_test( name = "Java7CompatibilityTest", size = "small", srcs = ["Java7CompatibilityTest.java"], deps = [ "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//third_party:asm", "//third_party:junit4", "//third_party:truth", @@ -411,18 +403,6 @@ java_test( ) java_test( - name = "IndexedInputsTest", - size = "small", - srcs = ["IndexedInputsTest.java"], - deps = [ - "//src/tools/android/java/com/google/devtools/build/android/desugar", - "//third_party:guava", - "//third_party:junit4", - "//third_party:truth", - ], -) - -java_test( name = "TryWithResourcesRewriterUnitTestWithReuseStrategy", size = "small", srcs = [ @@ -439,6 +419,7 @@ java_test( ":testdata", "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", "//third_party:asm", "//third_party:guava", @@ -464,6 +445,7 @@ java_test( ":testdata", "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", "//third_party:asm", "//third_party:guava", @@ -490,6 +472,7 @@ java_test( ":testdata", "//src/test/java/com/google/devtools/build/android/desugar/runtime:throwable_extension_test_utility", "//src/tools/android/java/com/google/devtools/build/android/desugar", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:throwable_extension", "//third_party:asm", "//third_party:guava", @@ -1971,6 +1954,7 @@ test_suite( ":windows_tests", "//src/test/java/com/google/devtools/build/android/desugar/classes_for_testing_type_inference:all_windows_tests", "//src/test/java/com/google/devtools/build/android/desugar/dependencies:all_windows_tests", + "//src/test/java/com/google/devtools/build/android/desugar/io:all_windows_tests", "//src/test/java/com/google/devtools/build/android/desugar/runtime:all_windows_tests", ], visibility = ["//src/test/java/com/google/devtools/build/android:__pkg__"], diff --git a/src/test/java/com/google/devtools/build/android/desugar/CoreLibrarySupportTest.java b/src/test/java/com/google/devtools/build/android/desugar/CoreLibrarySupportTest.java index ec4e16d8dd..42f1f78c97 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/CoreLibrarySupportTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/CoreLibrarySupportTest.java @@ -16,6 +16,7 @@ package com.google.devtools.build.android.desugar; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; import java.util.Collection; import java.util.Comparator; import java.util.Map; diff --git a/src/test/java/com/google/devtools/build/android/desugar/CorePackageRenamerTest.java b/src/test/java/com/google/devtools/build/android/desugar/CorePackageRenamerTest.java index 2bdd58bd8d..5220ed6f18 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/CorePackageRenamerTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/CorePackageRenamerTest.java @@ -16,6 +16,7 @@ package com.google.devtools.build.android.desugar; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java index faa6dda932..406a36f481 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixerTest.java @@ -19,7 +19,10 @@ import static com.google.devtools.build.android.desugar.DefaultMethodClassFixer. import com.google.common.collect.ImmutableList; import com.google.common.io.Closer; -import com.google.devtools.build.android.desugar.Desugar.ThrowingClassLoader; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; +import com.google.devtools.build.android.desugar.io.HeaderClassLoader; +import com.google.devtools.build.android.desugar.io.IndexedInputs; +import com.google.devtools.build.android.desugar.io.ThrowingClassLoader; import java.io.File; import java.io.IOException; import java.io.Serializable; diff --git a/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java b/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java index 2eab943aba..99e51c1551 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/Java7CompatibilityTest.java @@ -16,6 +16,7 @@ package com.google.devtools.build.android.desugar; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; +import com.google.devtools.build.android.desugar.io.BitFlags; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; diff --git a/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java b/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java index 37afae7236..dc0da22d47 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriterTest.java @@ -25,6 +25,7 @@ import static org.objectweb.asm.Opcodes.ASM5; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; +import com.google.devtools.build.android.desugar.io.BitFlags; import com.google.devtools.build.android.desugar.runtime.ThrowableExtension; import com.google.devtools.build.android.desugar.testdata.ClassUsingTryWithResources; import java.io.IOException; diff --git a/src/test/java/com/google/devtools/build/android/desugar/io/BUILD b/src/test/java/com/google/devtools/build/android/desugar/io/BUILD new file mode 100644 index 0000000000..d968a7b9b9 --- /dev/null +++ b/src/test/java/com/google/devtools/build/android/desugar/io/BUILD @@ -0,0 +1,53 @@ +# Description: +# Tests for the Java 8 desugaring tool for Android. +package( + default_testonly = 1, +) + +licenses(["notice"]) # Apache 2.0 + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["**"]), + visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__pkg__"], +) + +java_test( + name = "FieldInfoTest", + size = "small", + srcs = ["FieldInfoTest.java"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", + "//third_party:junit4", + "//third_party:truth", + ], +) + +java_test( + name = "IndexedInputsTest", + size = "small", + srcs = ["IndexedInputsTest.java"], + deps = [ + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) + +test_suite( + name = "windows_tests", + tags = [ + "-no_windows", + "-slow", + ], +) + +test_suite( + name = "all_windows_tests", + tests = [ + ":windows_tests", + ], + visibility = ["//src/test/java/com/google/devtools/build/android/desugar:__pkg__"], +) diff --git a/src/test/java/com/google/devtools/build/android/desugar/FieldInfoTest.java b/src/test/java/com/google/devtools/build/android/desugar/io/FieldInfoTest.java index afb2beacbd..0579822328 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/FieldInfoTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/io/FieldInfoTest.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import static com.google.common.truth.Truth.assertThat; diff --git a/src/test/java/com/google/devtools/build/android/desugar/IndexedInputsTest.java b/src/test/java/com/google/devtools/build/android/desugar/io/IndexedInputsTest.java index bac3fc92cd..81a4b31c3c 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/IndexedInputsTest.java +++ b/src/test/java/com/google/devtools/build/android/desugar/io/IndexedInputsTest.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import static com.google.common.truth.Truth.assertThat; diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java index 482c32a9ba..830364c06c 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata/CollectionReferences.java @@ -54,6 +54,10 @@ public class CollectionReferences { return result; } + public void expire(long before) { + dates.removeIf(d -> d.getTime() < before); + } + static { System.out.println("Hello!"); } diff --git a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt index 35744cefd9..60825763f5 100644 --- a/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt +++ b/src/test/java/com/google/devtools/build/android/desugar/scan/testdata_golden.txt @@ -18,6 +18,19 @@ -keep class java.lang.System { *** out; } +-keep class java.lang.invoke.CallSite { +} +-keep class java.lang.invoke.LambdaMetafactory { + *** metafactory(java.lang.invoke.MethodHandles$Lookup, java.lang.String, java.lang.invoke.MethodType, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, java.lang.invoke.MethodType); +} +-keep class java.lang.invoke.MethodHandle { +} +-keep class java.lang.invoke.MethodHandles { +} +-keep class java.lang.invoke.MethodHandles$Lookup { +} +-keep class java.lang.invoke.MethodType { +} -keep class java.util.AbstractList { } -keep class java.util.ArrayList { @@ -28,6 +41,7 @@ *** iterator(); } -keep class java.util.Collection { + *** removeIf(java.util.function.Predicate); } -keep class java.util.Date { <init>(long); @@ -44,3 +58,5 @@ *** get(int); *** iterator(); } +-keep class java.util.function.Predicate { +} diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD index a75afb2f04..55e3f8bd55 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/BUILD @@ -36,6 +36,7 @@ java_library( ":deps_collector_api", "//src/main/java/com/google/devtools/common/options", "//src/tools/android/java/com/google/devtools/build/android:android_builder_lib", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io", "//third_party:asm", "//third_party:asm-commons", "//third_party:asm-tree", @@ -58,6 +59,7 @@ filegroup( name = "srcs", srcs = glob(["**"]) + [ "//src/tools/android/java/com/google/devtools/build/android/desugar/dependencies:srcs", + "//src/tools/android/java/com/google/devtools/build/android/desugar/io:srcs", "//src/tools/android/java/com/google/devtools/build/android/desugar/runtime:srcs", ], visibility = ["//src/tools/android/java/com/google/devtools/build/android:__pkg__"], diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/BytecodeTypeInference.java b/src/tools/android/java/com/google/devtools/build/android/desugar/BytecodeTypeInference.java index 783069fee4..ce36071be4 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/BytecodeTypeInference.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/BytecodeTypeInference.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.ArrayList; import java.util.Optional; import javax.annotation.Nullable; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/ClassReaderFactory.java b/src/tools/android/java/com/google/devtools/build/android/desugar/ClassReaderFactory.java index bae5251d0b..aff9bab05d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/ClassReaderFactory.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/ClassReaderFactory.java @@ -13,6 +13,9 @@ // limitations under the License. package com.google.devtools.build.android.desugar; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; +import com.google.devtools.build.android.desugar.io.IndexedInputs; +import com.google.devtools.build.android.desugar.io.InputFileProvider; import java.io.IOException; import java.io.InputStream; import javax.annotation.Nullable; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/ClassVsInterface.java b/src/tools/android/java/com/google/devtools/build/android/desugar/ClassVsInterface.java index cb62deb4b0..272445451b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/ClassVsInterface.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/ClassVsInterface.java @@ -16,6 +16,7 @@ package com.google.devtools.build.android.desugar; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.HashMap; import javax.annotation.Nullable; import org.objectweb.asm.ClassReader; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java index da23c1268a..fd10e5e9f1 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibrarySupport.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; +import com.google.devtools.build.android.desugar.io.BitFlags; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; import com.google.errorprone.annotations.Immutable; import java.lang.reflect.Method; import java.util.Collection; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java index 853ed092ad..960cfeb106 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/DefaultMethodClassFixer.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableList; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; 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 506a380c12..c176f9cfc2 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 @@ -27,14 +27,19 @@ import com.google.common.io.ByteStreams; import com.google.common.io.Closer; import com.google.devtools.build.android.Converters.ExistingPathConverter; import com.google.devtools.build.android.Converters.PathConverter; -import com.google.devtools.build.android.desugar.CoreLibraryRewriter.UnprefixingClassWriter; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter.UnprefixingClassWriter; +import com.google.devtools.build.android.desugar.io.HeaderClassLoader; +import com.google.devtools.build.android.desugar.io.IndexedInputs; +import com.google.devtools.build.android.desugar.io.InputFileProvider; +import com.google.devtools.build.android.desugar.io.OutputFileProvider; +import com.google.devtools.build.android.desugar.io.ThrowingClassLoader; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.ShellQuotedParamsFilePreProcessor; -import com.google.errorprone.annotations.MustBeClosed; import java.io.IOError; import java.io.IOException; import java.io.InputStream; @@ -386,8 +391,8 @@ class Desugar { Files.isDirectory(inputPath) || !Files.isDirectory(outputPath), "Input jar file requires an output jar file"); - try (OutputFileProvider outputFileProvider = toOutputFileProvider(outputPath); - InputFileProvider inputFiles = toInputFileProvider(inputPath)) { + try (OutputFileProvider outputFileProvider = OutputFileProvider.create(outputPath); + InputFileProvider inputFiles = InputFileProvider.open(inputPath)) { DependencyCollector depsCollector = createDepsCollector(); IndexedInputs indexedInputFiles = new IndexedInputs(ImmutableList.of(inputFiles)); // Prepend classpath with input file itself so LambdaDesugaring can load classes with @@ -942,19 +947,6 @@ class Desugar { return ioPairListbuilder.build(); } - @VisibleForTesting - static class ThrowingClassLoader extends ClassLoader { - @Override - protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (name.startsWith("java.")) { - // Use system class loader for java. classes, since ClassLoader.defineClass gets - // grumpy when those don't come from the standard place. - return super.loadClass(name, resolve); - } - throw new ClassNotFoundException(); - } - } - private static void deleteTreeOnExit(final Path directory) { Thread shutdownHook = new Thread() { @@ -993,26 +985,6 @@ class Desugar { } } - /** Transform a Path to an {@link OutputFileProvider} */ - @MustBeClosed - private static OutputFileProvider toOutputFileProvider(Path path) throws IOException { - if (Files.isDirectory(path)) { - return new DirectoryOutputFileProvider(path); - } else { - return new ZipOutputFileProvider(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 new DirectoryInputFileProvider(path); - } else { - return new ZipInputFileProvider(path); - } - } - /** * Transform a list of Path to a list of InputFileProvider and register them with the given * closer. @@ -1023,7 +995,7 @@ class Desugar { Closer closer, List<Path> paths) throws IOException { ImmutableList.Builder<InputFileProvider> builder = new ImmutableList.Builder<>(); for (Path path : paths) { - builder.add(closer.register(toInputFileProvider(path))); + builder.add(closer.register(InputFileProvider.open(path))); } return builder.build(); } diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/EmulatedInterfaceRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/EmulatedInterfaceRewriter.java index f066f2a74c..355dd9713c 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/EmulatedInterfaceRewriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/EmulatedInterfaceRewriter.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.android.desugar; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.Collections; import java.util.LinkedHashSet; import org.objectweb.asm.ClassVisitor; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java index 0a10df1f43..e9e3199428 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/InterfaceDesugaring.java @@ -17,6 +17,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import com.google.devtools.build.android.desugar.io.BitFlags; +import com.google.devtools.build.android.desugar.io.FieldInfo; import java.lang.reflect.Method; import javax.annotation.Nullable; import org.objectweb.asm.AnnotationVisitor; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java b/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java index 37a45dd3c0..2090d5ccc2 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/Java7Compatibility.java @@ -17,6 +17,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import com.google.devtools.build.android.desugar.io.BitFlags; import javax.annotation.Nullable; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java index fb05bcbf91..6b0a9214ee 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaClassFixer.java @@ -19,6 +19,7 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.HashSet; import java.util.LinkedHashSet; import org.objectweb.asm.AnnotationVisitor; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java index 5f41347be5..f9b531698d 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/LambdaDesugaring.java @@ -21,6 +21,7 @@ import static org.objectweb.asm.Opcodes.ASM6; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/LongCompareMethodRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/LongCompareMethodRewriter.java index 6ac415df5b..7f2f35525f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/LongCompareMethodRewriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/LongCompareMethodRewriter.java @@ -17,6 +17,7 @@ import static org.objectweb.asm.Opcodes.ASM6; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.LCMP; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/ObjectsRequireNonNullMethodRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/ObjectsRequireNonNullMethodRewriter.java index 5e0a3441a7..931459ae0e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/ObjectsRequireNonNullMethodRewriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/ObjectsRequireNonNullMethodRewriter.java @@ -19,6 +19,7 @@ import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.POP; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java index e8509e71d1..818585f20f 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java @@ -29,6 +29,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.android.desugar.BytecodeTypeInference.InferredType; +import com.google.devtools.build.android.desugar.io.BitFlags; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Optional; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD new file mode 100644 index 0000000000..7b84a94c0d --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BUILD @@ -0,0 +1,25 @@ +# Description: +# Tool for desugaring Java constructs not supported by Android tools or devices. + +java_library( + name = "io", + srcs = glob(["*.java"]), + visibility = [ + "//src/test/java/com/google/devtools/build/android/desugar:__subpackages__", + "//src/tools/android/java/com/google/devtools/build/android/desugar:__pkg__", + ], + deps = [ + "//third_party:asm", + "//third_party:asm-commons", + "//third_party:asm-tree", + "//third_party:auto_value", + "//third_party:guava", + "//third_party:jsr305", + ], +) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src/tools/android/java/com/google/devtools/build/android/desugar:__pkg__"], +) diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/BitFlags.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BitFlags.java index 8be228801d..af6f48130e 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/BitFlags.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/BitFlags.java @@ -11,12 +11,12 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import org.objectweb.asm.Opcodes; /** Convenience method for working with {@code int} bitwise flags. */ -class BitFlags { +public class BitFlags { /** * Returns {@code true} iff <b>all</b> bits in {@code bitmask} are set in {@code flags}. Trivially diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java index 698fc53a39..f3c546c1c3 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/CoreLibraryRewriter.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/CoreLibraryRewriter.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import java.io.IOException; import java.io.InputStream; @@ -25,7 +25,7 @@ import org.objectweb.asm.commons.ClassRemapper; import org.objectweb.asm.commons.Remapper; /** Utility class to prefix or unprefix class names of core library classes */ -class CoreLibraryRewriter { +public class CoreLibraryRewriter { private final String prefix; public CoreLibraryRewriter(String prefix) { @@ -178,11 +178,11 @@ class CoreLibraryRewriter { /** Returns the (unprefixed) name of the class once written. */ @Nullable - String getClassName() { + public String getClassName() { return finalClassName; } - byte[] toByteArray() { + public byte[] toByteArray() { return writer.toByteArray(); } diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/DirectoryInputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/DirectoryInputFileProvider.java index 1c5abc9dca..c607b4233b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/DirectoryInputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/DirectoryInputFileProvider.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import java.io.File; import java.io.FileInputStream; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/DirectoryOutputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/DirectoryOutputFileProvider.java index 782a81ea2b..f8e87cbb33 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/DirectoryOutputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/DirectoryOutputFileProvider.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import com.google.common.io.ByteStreams; import java.io.IOException; @@ -21,7 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; /** Output provider is a directory. */ -public class DirectoryOutputFileProvider implements OutputFileProvider { +class DirectoryOutputFileProvider implements OutputFileProvider { private final Path root; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/FieldInfo.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/FieldInfo.java index c281039f61..0b4f63418b 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/FieldInfo.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/FieldInfo.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import com.google.auto.value.AutoValue; @@ -19,7 +19,7 @@ import com.google.auto.value.AutoValue; @AutoValue public abstract class FieldInfo { - static FieldInfo create(String owner, String name, String desc) { + public static FieldInfo create(String owner, String name, String desc) { return new AutoValue_FieldInfo(owner, name, desc); } diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/HeaderClassLoader.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/HeaderClassLoader.java index 77d99bb01c..f70dc0e3e2 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/HeaderClassLoader.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/HeaderClassLoader.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import com.google.common.collect.ImmutableList; import java.io.IOError; @@ -33,7 +33,7 @@ import org.objectweb.asm.Opcodes; * * @see java.net.URLClassLoader */ -class HeaderClassLoader extends ClassLoader { +public class HeaderClassLoader extends ClassLoader { private final IndexedInputs indexedInputs; private final CoreLibraryRewriter rewriter; 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/io/IndexedInputs.java index 33c6132020..8ce4b62302 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/io/IndexedInputs.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; @@ -28,7 +28,7 @@ import javax.annotation.Nullable; * scanning all inputs over and over for each class to load. An indexed inputs can have a parent * that is firstly used when a file name is searched. */ -class IndexedInputs { +public class IndexedInputs { private final ImmutableMap<String, InputFileProvider> inputFiles; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/InputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/InputFileProvider.java index c2b63532da..c41d018df8 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/InputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/InputFileProvider.java @@ -11,15 +11,18 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; +import com.google.errorprone.annotations.MustBeClosed; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.zip.ZipEntry; /** Input file provider allows to iterate on relative path filename of a directory or a jar file. */ -interface InputFileProvider extends Closeable, Iterable<String> { +public interface InputFileProvider extends Closeable, Iterable<String> { /** * Return a ZipEntry for {@code filename}. If the provider is a {@link ZipInputFileProvider}, the @@ -33,4 +36,14 @@ interface InputFileProvider extends Closeable, Iterable<String> { * responsibility of the caller to close this stream. */ InputStream getInputStream(String filename) throws IOException; + + /** Transform a Path to an InputFileProvider that needs to be closed by the caller. */ + @MustBeClosed + public static InputFileProvider open(Path path) throws IOException { + if (Files.isDirectory(path)) { + return new DirectoryInputFileProvider(path); + } else { + return new ZipInputFileProvider(path); + } + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/OutputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/OutputFileProvider.java index 7a590ef2ba..e693786082 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/OutputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/OutputFileProvider.java @@ -11,12 +11,15 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; +import com.google.errorprone.annotations.MustBeClosed; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; /** Output file provider allows to write files in directory or jar files. */ -interface OutputFileProvider extends AutoCloseable { +public interface OutputFileProvider extends AutoCloseable { /** Filename to use to write out dependency metadata for later consistency checking. */ public static final String DESUGAR_DEPS_FILENAME = "META-INF/desugar_deps"; @@ -29,4 +32,14 @@ interface OutputFileProvider extends AutoCloseable { /** Write {@code content} in {@code filename} to this output */ void write(String filename, byte[] content) throws IOException; + + /** Transform a Path to an {@link OutputFileProvider} */ + @MustBeClosed + public static OutputFileProvider create(Path path) throws IOException { + if (Files.isDirectory(path)) { + return new DirectoryOutputFileProvider(path); + } else { + return new ZipOutputFileProvider(path); + } + } } diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/io/ThrowingClassLoader.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ThrowingClassLoader.java new file mode 100644 index 0000000000..16f83f22e4 --- /dev/null +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ThrowingClassLoader.java @@ -0,0 +1,27 @@ +// Copyright 2016 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar.io; + +/** Class loader that throws whenever it can, for use the parent of a class loader hierarchy. */ +public class ThrowingClassLoader extends ClassLoader { + @Override + protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("java.")) { + // Use system class loader for java. classes, since ClassLoader.defineClass gets + // grumpy when those don't come from the standard place. + return super.loadClass(name, resolve); + } + throw new ClassNotFoundException(); + } +}
\ No newline at end of file diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/ZipInputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipInputFileProvider.java index 307c8b83e6..9bd7758952 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/ZipInputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipInputFileProvider.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import com.google.common.base.Functions; import com.google.common.collect.Iterators; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/ZipOutputFileProvider.java b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java index 8d6501d198..36cb26d157 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/ZipOutputFileProvider.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/io/ZipOutputFileProvider.java @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -package com.google.devtools.build.android.desugar; +package com.google.devtools.build.android.desugar.io; import static com.google.common.base.Preconditions.checkArgument; @@ -26,7 +26,7 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** Output provider is a zip file. */ -public class ZipOutputFileProvider implements OutputFileProvider { +class ZipOutputFileProvider implements OutputFileProvider { private final ZipOutputStream out; diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/scan/KeepScanner.java b/src/tools/android/java/com/google/devtools/build/android/desugar/scan/KeepScanner.java index b347c7a659..4924f7c002 100644 --- a/src/tools/android/java/com/google/devtools/build/android/desugar/scan/KeepScanner.java +++ b/src/tools/android/java/com/google/devtools/build/android/desugar/scan/KeepScanner.java @@ -15,13 +15,21 @@ package com.google.devtools.build.android.desugar.scan; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; import static java.nio.file.StandardOpenOption.CREATE; import static java.util.Comparator.comparing; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.io.ByteStreams; +import com.google.common.io.Closer; import com.google.devtools.build.android.Converters.ExistingPathConverter; import com.google.devtools.build.android.Converters.PathConverter; +import com.google.devtools.build.android.desugar.io.CoreLibraryRewriter; +import com.google.devtools.build.android.desugar.io.HeaderClassLoader; +import com.google.devtools.build.android.desugar.io.IndexedInputs; +import com.google.devtools.build.android.desugar.io.InputFileProvider; +import com.google.devtools.build.android.desugar.io.ThrowingClassLoader; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; @@ -32,9 +40,11 @@ import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; +import java.lang.reflect.Method; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.zip.ZipEntry; @@ -57,6 +67,32 @@ class KeepScanner { public Path inputJars; @Option( + name = "classpath_entry", + allowMultiple = true, + defaultValue = "", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + converter = ExistingPathConverter.class, + help = + "Ordered classpath (Jar or directory) to resolve symbols in the --input Jar, like " + + "javac's -cp flag." + ) + public List<Path> classpath; + + @Option( + name = "bootclasspath_entry", + allowMultiple = true, + defaultValue = "", + documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, + effectTags = {OptionEffectTag.UNKNOWN}, + converter = ExistingPathConverter.class, + help = + "Bootclasspath that was used to compile the --input Jar with, like javac's " + + "-bootclasspath flag (required)." + ) + public List<Path> bootclasspath; + + @Option( name = "keep_file", defaultValue = "null", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, @@ -81,10 +117,25 @@ class KeepScanner { parser.setAllowResidue(false); parser.enableParamsFileSupport(new ShellQuotedParamsFilePreProcessor(FileSystems.getDefault())); parser.parseAndExitUponError(args); - KeepScannerOptions options = parser.getOptions(KeepScannerOptions.class); - Map<String, ImmutableSet<KeepReference>> seeds = - scan(checkNotNull(options.inputJars), options.prefix); + + Map<String, ImmutableSet<KeepReference>> seeds; + try (Closer closer = Closer.create()) { + // TODO(kmb): Try to share more of this code with Desugar binary + IndexedInputs classpath = + new IndexedInputs(toRegisteredInputFileProvider(closer, options.classpath)); + IndexedInputs bootclasspath = + new IndexedInputs(toRegisteredInputFileProvider(closer, options.bootclasspath)); + + // Construct classloader from classpath. Since we're assuming the prefix we're looking for + // isn't part of the input itself we shouldn't need to include the input in the classloader. + CoreLibraryRewriter noopRewriter = new CoreLibraryRewriter(""); + ClassLoader classloader = + new HeaderClassLoader(classpath, noopRewriter, + new HeaderClassLoader(bootclasspath, noopRewriter, + new ThrowingClassLoader())); + seeds = scan(checkNotNull(options.inputJars), options.prefix, classloader); + } try (PrintStream out = new PrintStream( @@ -117,11 +168,9 @@ class KeepScanner { }); } - /** - * Scans for and returns references with owners matching the given prefix grouped by owner. - */ - private static Map<String, ImmutableSet<KeepReference>> scan(Path jarFile, String prefix) - throws IOException { + /** Scans for and returns references with owners matching the given prefix grouped by owner. */ + private static Map<String, ImmutableSet<KeepReference>> scan( + Path jarFile, String prefix, ClassLoader classpath) throws IOException { // We read the Jar sequentially since ZipFile uses locks anyway but then allow scanning each // class in parallel. try (ZipFile zip = new ZipFile(jarFile.toFile())) { @@ -131,6 +180,8 @@ class KeepScanner { .parallel() .flatMap( content -> PrefixReferenceScanner.scan(new ClassReader(content), prefix).stream()) + .distinct() // so we don't process the same reference multiple times next + .map(ref -> nearestDeclaration(ref, classpath)) .collect( Collectors.groupingByConcurrent( KeepReference::internalName, ImmutableSet.toImmutableSet())); @@ -147,6 +198,68 @@ class KeepScanner { } } + /** + * Find the nearest definition of the given reference in the class hierarchy and return the + * modified reference. This is needed b/c bytecode sometimes refers to a method or field using + * an owner type that inherits the method or field instead of defining the member itself. + * In that case we need to find and keep the inherited definition. + */ + private static KeepReference nearestDeclaration(KeepReference ref, ClassLoader classpath) { + if (!ref.isMemberReference() || "<init>".equals(ref.name())) { + return ref; // class and constructor references don't need any further work + } + + Class<?> clazz; + try { + clazz = classpath.loadClass(ref.internalName().replace('/', '.')); + } catch (ClassNotFoundException e) { + throw (NoClassDefFoundError) new NoClassDefFoundError("Couldn't load " + ref).initCause(e); + } + + Class<?> owner = findDeclaringClass(clazz, ref); + if (owner == clazz) { + return ref; + } + String parent = checkNotNull(owner, "Can't resolve: %s", ref).getName().replace('.', '/'); + return KeepReference.memberReference(parent, ref.name(), ref.desc()); + } + + private static Class<?> findDeclaringClass(Class<?> clazz, KeepReference ref) { + if (ref.isFieldReference()) { + try { + return clazz.getField(ref.name()).getDeclaringClass(); + } catch (NoSuchFieldException e) { + // field must be non-public, so search class hierarchy + do { + try { + return clazz.getDeclaredField(ref.name()).getDeclaringClass(); + } catch (NoSuchFieldException ignored) { + // fall through for clarity + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + } + } else { + checkState(ref.isMethodReference()); + Type descriptor = Type.getMethodType(ref.desc()); + for (Method m : clazz.getMethods()) { + if (m.getName().equals(ref.name()) && Type.getType(m).equals(descriptor)) { + return m.getDeclaringClass(); + } + } + do { + // Method must be non-public, so search class hierarchy + for (Method m : clazz.getDeclaredMethods()) { + if (m.getName().equals(ref.name()) && Type.getType(m).equals(descriptor)) { + return m.getDeclaringClass(); + } + } + clazz = clazz.getSuperclass(); + } while (clazz != null); + } + return null; + } + private static CharSequence toKeepDescriptor(KeepReference member) { StringBuilder result = new StringBuilder(); if (member.isMethodReference()) { @@ -172,5 +285,19 @@ class KeepScanner { return result; } + /** + * Transform a list of Path to a list of InputFileProvider and register them with the given + * closer. + */ + @SuppressWarnings("MustBeClosedChecker") + private static ImmutableList<InputFileProvider> toRegisteredInputFileProvider( + Closer closer, List<Path> paths) throws IOException { + ImmutableList.Builder<InputFileProvider> builder = new ImmutableList.Builder<>(); + for (Path path : paths) { + builder.add(closer.register(InputFileProvider.open(path))); + } + return builder.build(); + } + private KeepScanner() {} } |