Version 2.0.28
Cherry-pick: Provide an API to retreive the list of backported methods
CL: https://r8-review.googlesource.com/c/r8/+/48310
Cherry-pick: Extend list of backported methoods
CL: https://r8-review.googlesource.com/c/r8/+/48428
Bug: 140368601
Bug: 140367927
Change-Id: Icec3a85d2c47d3209eeff3e7495cf209fd9e777e
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodList.java b/src/main/java/com/android/tools/r8/BackportedMethodList.java
new file mode 100644
index 0000000..befc767
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BackportedMethodList.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Tool to extract the list of methods which is backported by the D8 and R8 compilers.
+ *
+ * <p>The D8 and R8 compilers will backport some simple Java APIs which are not present on all API
+ * levels. One example of this is the static method <code>int Integer.divideUnsigned(int a, int b)
+ * </code> which was added from API level 26.
+ *
+ * <p>As these backported methods is supported on all API levels, tools like linters and code
+ * checkers need this information to avoid false negatives when analyzing code.
+ *
+ * <p>This tool will generate a list of all the backported methods for the associated version of D8
+ * and R8.
+ *
+ * <p>This tool will <strong>not</strong> provide information about the APIs supported when using
+ * library desugaring. That information is provided in the dependencies used for library desugaring.
+ * However, in place of library desugaring backporting will be able to backport additional methods.
+ * If library desugaring is used, then passing information about that to this tool will provide the
+ * more precise list. See b/149078312.
+ *
+ * <p>The tool is invoked by calling {@link #run(BackportedMethodListCommand)
+ * BackportedMethodList.run} with an appropriate {@link BackportedMethodListCommand}.
+ *
+ * <p>For example:
+ *
+ * <pre>
+ * BackportedMethodList.run(BackportedMethodListCommand.builder()
+ * .setMinApiLevel(apiLevel)
+ * .setOutputPath(Paths.get("methods-list.txt"))
+ * .build());
+ * </pre>
+ *
+ * The above generates the list of backported methods for a compilation with a min API of <code>
+ * apiLevel</code> into the file <code>methods-list.txt</code>.
+ */
+@Keep
+public class BackportedMethodList {
+
+ static final String USAGE_MESSAGE =
+ StringUtils.joinLines(
+ "Usage: BackportedMethodList [options]",
+ " Options are:",
+ " --output <file> # Output result in <file>.",
+ " --min-api <number> # Minimum Android API level for the application",
+ " --desugared-lib <file> # Desugared library configuration (JSON from the",
+ " # configuration)",
+ " --lib <file> # The compilation SDK library (android.jar)",
+ " --version # Print the version of BackportedMethodList.",
+ " --help # Print this message.");
+
+ private static String formatMethod(DexMethod method) {
+ return DescriptorUtils.getClassBinaryNameFromDescriptor(method.holder.descriptor.toString())
+ + '#'
+ + method.name
+ + method.proto.toDescriptorString();
+ }
+
+ public static void run(BackportedMethodListCommand command) throws CompilationFailedException {
+ if (command.isPrintHelp()) {
+ System.out.println(USAGE_MESSAGE);
+ return;
+ }
+ if (command.isPrintVersion()) {
+ System.out.println("BackportedMethodList " + Version.getVersionString());
+ return;
+ }
+ InternalOptions options = command.getInternalOptions();
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ try {
+ ExceptionUtils.withD8CompilationHandler(
+ command.getReporter(),
+ () -> {
+ BackportedMethodRewriter.generateListOfBackportedMethods(
+ command.getInputApp(), options, executorService)
+ .stream()
+ .map(BackportedMethodList::formatMethod)
+ .sorted()
+ .forEach(
+ formattedMethod ->
+ command
+ .getBackportedMethodListConsumer()
+ .accept(formattedMethod, command.getReporter()));
+ command.getBackportedMethodListConsumer().finished(command.getReporter());
+ });
+ } finally {
+ executorService.shutdown();
+ }
+ }
+
+ public static void run(String[] args) throws CompilationFailedException {
+ run(BackportedMethodListCommand.parse(args).build());
+ }
+
+ public static void main(String[] args) {
+ ExceptionUtils.withMainProgramHandler(() -> run(args));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
new file mode 100644
index 0000000..a4af022
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -0,0 +1,341 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringDiagnostic;
+import com.google.common.collect.ImmutableSet;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Immutable command structure for an invocation of the {@link BackportedMethodList} tool.
+ *
+ * <p>To build a BackportedMethodList command use the {@link BackportedMethodListCommand.Builder}
+ * class. For example:
+ *
+ * <pre>
+ * BackportedMethodListCommand command = BackportedMethodListCommand.builder()
+ * .setMinApiLevel(apiLevel)
+ * .setOutputPath(Paths.get("methods-list.txt"))
+ * .build();
+ * </pre>
+ */
+@Keep
+public class BackportedMethodListCommand {
+
+ private final boolean printHelp;
+ private final boolean printVersion;
+ private final Reporter reporter;
+ private final int minApiLevel;
+ private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private final AndroidApp app;
+ private final StringConsumer backportedMethodListConsumer;
+ private final DexItemFactory factory;
+
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
+ Reporter getReporter() {
+ return reporter;
+ }
+
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ public DesugaredLibraryConfiguration getDesugaredLibraryConfiguration() {
+ return desugaredLibraryConfiguration;
+ }
+
+ public StringConsumer getBackportedMethodListConsumer() {
+ return backportedMethodListConsumer;
+ }
+
+ AndroidApp getInputApp() {
+ return app;
+ }
+
+ private BackportedMethodListCommand(boolean printHelp, boolean printVersion) {
+ this.printHelp = printHelp;
+ this.printVersion = printVersion;
+ this.reporter = new Reporter();
+ this.minApiLevel = -1;
+ this.desugaredLibraryConfiguration = null;
+ this.app = null;
+ this.backportedMethodListConsumer = null;
+ this.factory = null;
+ }
+
+ private BackportedMethodListCommand(
+ Reporter reporter,
+ int minApiLevel,
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration,
+ AndroidApp app,
+ StringConsumer backportedMethodListConsumer,
+ DexItemFactory factory) {
+ this.printHelp = false;
+ this.printVersion = false;
+ this.reporter = reporter;
+ this.minApiLevel = minApiLevel;
+ this.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+ this.app = app;
+ this.backportedMethodListConsumer = backportedMethodListConsumer;
+ this.factory = factory;
+ }
+
+ InternalOptions getInternalOptions() {
+ InternalOptions options = new InternalOptions(factory, getReporter());
+ options.minApiLevel = minApiLevel;
+ options.desugaredLibraryConfiguration = desugaredLibraryConfiguration;
+ return options;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static Builder builder(DiagnosticsHandler diagnosticsHandler) {
+ return new Builder(diagnosticsHandler);
+ }
+
+ public static Builder parse(String[] args) {
+ final Set<String> OPTIONS_WITH_PARAMETER =
+ ImmutableSet.of("--output", "--min-api", "--desugared-lib", "--lib");
+
+ boolean hasDefinedApiLevel = false;
+ Builder builder = builder();
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i].trim();
+ String nextArg = null;
+ if (OPTIONS_WITH_PARAMETER.contains(arg)) {
+ if (++i < args.length) {
+ nextArg = args[i];
+ } else {
+ builder.error(new StringDiagnostic("Missing parameter for " + args[i - 1] + "."));
+ break;
+ }
+ }
+ if (arg.equals("--help")) {
+ builder.setPrintHelp(true);
+ } else if (arg.equals("--version")) {
+ builder.setPrintVersion(true);
+ } else if (arg.equals("--min-api")) {
+ if (hasDefinedApiLevel) {
+ builder.error(new StringDiagnostic("Cannot set multiple --min-api options"));
+ } else {
+ parseMinApi(builder, nextArg);
+ hasDefinedApiLevel = true;
+ }
+ } else if (arg.equals("--desugared-lib")) {
+ builder.addDesugaredLibraryConfiguration(StringResource.fromFile(Paths.get(nextArg)));
+ } else if (arg.equals("--lib")) {
+ builder.addLibraryFiles(Paths.get(nextArg));
+ } else if (arg.equals("--output")) {
+ builder.setOutputPath(Paths.get(nextArg));
+ } else {
+ builder.error(new StringDiagnostic("Unknown option: " + arg));
+ }
+ }
+ return builder;
+ }
+
+ private static void parseMinApi(Builder builder, String minApiString) {
+ int minApi;
+ try {
+ minApi = Integer.parseInt(minApiString);
+ } catch (NumberFormatException e) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString));
+ return;
+ }
+ if (minApi < 1) {
+ builder.error(new StringDiagnostic("Invalid argument to --min-api: " + minApiString));
+ return;
+ }
+ builder.setMinApiLevel(minApi);
+ }
+
+ @Keep
+ public static class Builder {
+
+ private final Reporter reporter;
+ private int minApiLevel = AndroidApiLevel.B.getLevel();
+ private List<StringResource> desugaredLibraryConfigurationResources = new ArrayList<>();
+ private final AndroidApp.Builder app;
+ private StringConsumer backportedMethodListConsumer;
+ private boolean printHelp = false;
+ private boolean printVersion = false;
+
+ private Builder() {
+ this(new DiagnosticsHandler() {});
+ }
+
+ private Builder(DiagnosticsHandler diagnosticsHandler) {
+ this.app = AndroidApp.builder();
+ this.reporter = new Reporter(diagnosticsHandler);
+ }
+
+ /**
+ * Set the minimum API level for the application compiled.
+ *
+ * <p>The tool will only report backported methods which are not present at this API level.
+ *
+ * <p>The default is 1 if never set.
+ */
+ public Builder setMinApiLevel(int minApiLevel) {
+ if (minApiLevel <= 0) {
+ reporter.error(new StringDiagnostic("Invalid minApiLevel: " + minApiLevel));
+ } else {
+ this.minApiLevel = minApiLevel;
+ }
+ return this;
+ }
+
+ public int getMinApiLevel() {
+ return minApiLevel;
+ }
+
+ /** Desugared library configuration */
+ public Builder addDesugaredLibraryConfiguration(StringResource configuration) {
+ desugaredLibraryConfigurationResources.add(configuration);
+ return this;
+ }
+
+ /** Desugared library configuration */
+ public Builder addDesugaredLibraryConfiguration(String configuration) {
+ return addDesugaredLibraryConfiguration(
+ StringResource.fromString(configuration, Origin.unknown()));
+ }
+
+ /** The compilation SDK library (android.jar) */
+ public Builder addLibraryResourceProvider(ClassFileResourceProvider provider) {
+ app.addLibraryResourceProvider(provider);
+ return this;
+ }
+
+ /** The compilation SDK library (android.jar) */
+ public Builder addLibraryFiles(Path... files) {
+ addLibraryFiles(Arrays.asList(files));
+ return this;
+ }
+
+ /** The compilation SDK library (android.jar) */
+ public Builder addLibraryFiles(Collection<Path> files) {
+ for (Path path : files) {
+ app.addLibraryFile(path);
+ }
+ return this;
+ }
+
+ DesugaredLibraryConfiguration getDesugaredLibraryConfiguration(DexItemFactory factory) {
+ if (desugaredLibraryConfigurationResources.isEmpty()) {
+ return DesugaredLibraryConfiguration.empty();
+ }
+ if (desugaredLibraryConfigurationResources.size() > 1) {
+ reporter.fatalError("Only one desugared library configuration is supported.");
+ }
+ StringResource desugaredLibraryConfigurationResource =
+ desugaredLibraryConfigurationResources.get(0);
+ DesugaredLibraryConfigurationParser libraryParser =
+ new DesugaredLibraryConfigurationParser(factory, null, false, getMinApiLevel());
+ return libraryParser.parse(desugaredLibraryConfigurationResource);
+ }
+
+ /** Output file for the backported method list */
+ public Builder setOutputPath(Path outputPath) {
+ backportedMethodListConsumer =
+ new StringConsumer.FileConsumer(outputPath) {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ super.accept(string, handler);
+ super.accept(System.lineSeparator(), handler);
+ }
+ };
+ return this;
+ }
+
+ /** Consumer receiving the the backported method list */
+ public Builder setConsumer(StringConsumer consumer) {
+ this.backportedMethodListConsumer = consumer;
+ return this;
+ }
+
+ /** True if the print-help flag is enabled. */
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ /** Set the value of the print-help flag. */
+ public Builder setPrintHelp(boolean printHelp) {
+ this.printHelp = printHelp;
+ return this;
+ }
+
+ /** True if the print-version flag is enabled. */
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
+ /** Set the value of the print-version flag. */
+ public Builder setPrintVersion(boolean printVersion) {
+ this.printVersion = printVersion;
+ return this;
+ }
+
+ private void error(Diagnostic diagnostic) {
+ reporter.error(diagnostic);
+ }
+
+ public BackportedMethodListCommand build() {
+ AndroidApp library = app.build();
+ if (!desugaredLibraryConfigurationResources.isEmpty()
+ && library.getLibraryResourceProviders().isEmpty()) {
+ reporter.error(
+ new StringDiagnostic("With desugared library configuration a library is required"));
+ }
+
+ if (isPrintHelp() || isPrintVersion()) {
+ return new BackportedMethodListCommand(isPrintHelp(), isPrintVersion());
+ }
+
+ if (backportedMethodListConsumer == null) {
+ backportedMethodListConsumer =
+ new StringConsumer() {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ System.out.println(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {}
+ };
+ }
+ DexItemFactory factory = new DexItemFactory();
+ return new BackportedMethodListCommand(
+ reporter,
+ minApiLevel,
+ getDesugaredLibraryConfiguration(factory),
+ library,
+ backportedMethodListConsumer,
+ factory);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java b/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java
deleted file mode 100644
index 90f577b..0000000
--- a/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8;
-
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.utils.AndroidApiLevel;
-
-public class GenerateBackportedMethodList {
-
- public static void main(String[] args) {
- BackportedMethodRewriter.generateListOfBackportedMethods(AndroidApiLevel.B).stream()
- .map(DexMethod::toSourceString)
- .sorted()
- .forEach(System.out::println);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/SwissArmyKnife.java b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
index d13081c..89aa64e 100644
--- a/src/main/java/com/android/tools/r8/SwissArmyKnife.java
+++ b/src/main/java/com/android/tools/r8/SwissArmyKnife.java
@@ -79,6 +79,9 @@
case "l8":
L8.main(shift(args));
break;
+ case "backportedmethods":
+ BackportedMethodList.main(shift(args));
+ break;
default:
runDefault(args);
break;
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 076b8a3..b3252ab 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 = "2.0.27";
+ public static final String LABEL = "2.0.28";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 8c632a2..5d1671b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -4,12 +4,18 @@
package com.android.tools.r8.ir.desugar;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
+import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
@@ -43,10 +49,13 @@
import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -81,15 +90,29 @@
this.rewritableMethods = new RewritableMethods(appView.options(), appView);
}
- public static List<DexMethod> generateListOfBackportedMethods(AndroidApiLevel apiLevel) {
- List<DexMethod> methods = new ArrayList<>();
- InternalOptions options = new InternalOptions();
- options.minApiLevel = apiLevel.getLevel();
- AppView<?> appView = AppView.createForD8(null, options);
- BackportedMethodRewriter.RewritableMethods rewritableMethods =
- new BackportedMethodRewriter.RewritableMethods(options, appView);
- rewritableMethods.visit(methods::add);
- return methods;
+ public static List<DexMethod> generateListOfBackportedMethods(
+ AndroidApp androidApp, InternalOptions options, ExecutorService executor) throws IOException {
+ try {
+ List<DexMethod> methods = new ArrayList<>();
+ PrefixRewritingMapper rewritePrefix =
+ options.desugaredLibraryConfiguration.createPrefixRewritingMapper(options);
+ AppInfo appInfo = null;
+ if (androidApp != null) {
+ DexApplication app =
+ new ApplicationReader(androidApp, options, Timing.empty()).read(executor);
+ appInfo =
+ options.desugaredLibraryConfiguration.getRewritePrefix().isEmpty()
+ ? new AppInfo(app)
+ : new AppInfoWithClassHierarchy(app);
+ }
+ AppView<?> appView = AppView.createForD8(appInfo, options, rewritePrefix);
+ BackportedMethodRewriter.RewritableMethods rewritableMethods =
+ new BackportedMethodRewriter.RewritableMethods(options, appView);
+ rewritableMethods.visit(methods::add);
+ return methods;
+ } catch (ExecutionException e) {
+ throw unwrapExecutionException(e);
+ }
}
public void desugar(IRCode code) {
diff --git a/src/test/java/com/android/tools/r8/BackportedMethodListTest.java b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
new file mode 100644
index 0000000..bb344d7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/BackportedMethodListTest.java
@@ -0,0 +1,154 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class BackportedMethodListTest {
+
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ enum Mode {
+ NO_LIBRARY,
+ LIBRARY,
+ LIBRARY_DESUGAR
+ }
+
+ @Parameterized.Parameters(name = "Mode: {0}")
+ public static Object[] data() {
+ return Mode.values();
+ }
+
+ private final Mode mode;
+
+ public BackportedMethodListTest(Mode mode) {
+ this.mode = mode;
+ }
+
+ private static class ListStringConsumer implements StringConsumer {
+ List<String> strings = new ArrayList<>();
+ boolean finished = false;
+
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ strings.add(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ finished = true;
+ }
+ }
+
+ private void checkContent(int apiLevel, List<String> backports) {
+ // Java 8 methods added at various API levels.
+ assertEquals(
+ apiLevel < AndroidApiLevel.K.getLevel(), backports.contains("java/lang/Byte#compare(BB)I"));
+ assertEquals(
+ apiLevel < AndroidApiLevel.N.getLevel(),
+ backports.contains("java/lang/Integer#hashCode(I)I"));
+ assertEquals(
+ apiLevel < AndroidApiLevel.O.getLevel(),
+ backports.contains("java/lang/Short#toUnsignedLong(S)J"));
+
+ // Java 9, 10 and 11 Optional methods which require Android N or library desugaring.
+ assertEquals(
+ mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
+ backports.contains(
+ "java/util/Optional#or(Ljava/util/function/Supplier;)Ljava/util/Optional;"));
+ assertEquals(
+ mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
+ backports.contains("java/util/OptionalInt#orElseThrow()I"));
+ assertEquals(
+ mode == Mode.LIBRARY_DESUGAR || apiLevel >= AndroidApiLevel.N.getLevel(),
+ backports.contains("java/util/OptionalLong#isEmpty()Z"));
+
+ // Java 9, 10 and 11 methods.
+ assertTrue(backports.contains("java/lang/StrictMath#multiplyExact(JI)J"));
+ assertTrue(backports.contains("java/util/List#copyOf(Ljava/util/Collection;)Ljava/util/List;"));
+ assertTrue(backports.contains("java/lang/Character#toString(I)Ljava/lang/String;"));
+ }
+
+ private void addLibraryDesugaring(BackportedMethodListCommand.Builder builder) {
+ builder
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+ }
+
+ @Test
+ public void testConsumer() throws Exception {
+ for (int apiLevel = 1; apiLevel < AndroidApiLevel.LATEST.getLevel(); apiLevel++) {
+ ListStringConsumer consumer = new ListStringConsumer();
+ BackportedMethodListCommand.Builder builder =
+ BackportedMethodListCommand.builder().setMinApiLevel(apiLevel).setConsumer(consumer);
+ if (mode == Mode.LIBRARY) {
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+ } else if (mode == Mode.LIBRARY_DESUGAR) {
+ addLibraryDesugaring(builder);
+ }
+ BackportedMethodList.run(builder.build());
+ assertTrue(consumer.finished);
+ checkContent(apiLevel, consumer.strings);
+ }
+ }
+
+ @Test
+ public void testFile() throws Exception {
+ for (int apiLevel = 1; apiLevel < AndroidApiLevel.LATEST.getLevel(); apiLevel++) {
+ Path output = temp.newFile().toPath();
+ BackportedMethodListCommand.Builder builder =
+ BackportedMethodListCommand.builder().setMinApiLevel(apiLevel).setOutputPath(output);
+ if (mode == Mode.LIBRARY) {
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P.getLevel()));
+ } else if (mode == Mode.LIBRARY_DESUGAR) {
+ addLibraryDesugaring(builder);
+ }
+ BackportedMethodList.run(builder.build());
+ checkContent(apiLevel, Files.readAllLines(output));
+ }
+ }
+
+ @Test
+ public void testFullList() throws Exception {
+ Assume.assumeTrue(mode == Mode.NO_LIBRARY);
+ ListStringConsumer consumer = new ListStringConsumer();
+ // Not setting neither min API level not library should produce the full list.
+ BackportedMethodList.run(BackportedMethodListCommand.builder().setConsumer(consumer).build());
+ assertTrue(consumer.finished);
+ checkContent(1, consumer.strings);
+ }
+
+ @Test
+ public void requireLibraryForDesugar() {
+ Assume.assumeTrue(mode == Mode.LIBRARY_DESUGAR);
+ // Require library when a desugar configuration is passed.
+ try {
+ BackportedMethodList.run(
+ BackportedMethodListCommand.builder()
+ .addDesugaredLibraryConfiguration(
+ StringResource.fromFile(ToolHelper.DESUGAR_LIB_JSON_FOR_TESTING))
+ .setConsumer(new ListStringConsumer())
+ .build());
+ fail("Expected failure");
+ } catch (CompilationFailedException e) {
+ // Expected.
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 2c7cbe5..83682b2 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -34,8 +36,11 @@
// Check that the backported methods for each API level are are not present in the
// android.jar for that level.
CodeInspector inspector = new CodeInspector(ToolHelper.getAndroidJar(apiLevel));
+ InternalOptions options = new InternalOptions();
+ options.minApiLevel = apiLevel.getLevel();
List<DexMethod> backportedMethods =
- BackportedMethodRewriter.generateListOfBackportedMethods(apiLevel);
+ BackportedMethodRewriter.generateListOfBackportedMethods(
+ null, options, ThreadUtils.getExecutorService(options));
for (DexMethod method : backportedMethods) {
// Two different DexItemFactories are in play, but as toSourceString is used for lookup
// that is not an issue.