Add a simple command line tool to get the list of backported APIs
Bug: 140367927
Change-Id: Ib6580a71738fefa76de0458330e32df71c85f032
diff --git a/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java b/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java
new file mode 100644
index 0000000..90f577b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/GenerateBackportedMethodList.java
@@ -0,0 +1,19 @@
+// 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/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index df3b9b7..14dbbbd 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
@@ -59,6 +59,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
public final class BackportedMethodRewriter {
@@ -78,7 +79,7 @@
this.appView = appView;
this.converter = converter;
this.factory = appView.dexItemFactory();
- this.rewritableMethods = new RewritableMethods(appView);
+ this.rewritableMethods = new RewritableMethods(appView.options(), appView);
Map<String, String> backportCoreLibraryMembers =
appView.options().desugaredLibraryConfiguration.getBackportCoreLibraryMember();
for (String coreLibMember : backportCoreLibraryMembers.keySet()) {
@@ -91,6 +92,16 @@
}
}
+ public static List<DexMethod> generateListOfBackportedMethods(AndroidApiLevel apiLevel) {
+ List<DexMethod> methods = new ArrayList<>();
+ InternalOptions options = new InternalOptions();
+ options.minApiLevel = apiLevel.getLevel();
+ BackportedMethodRewriter.RewritableMethods rewritableMethods =
+ new BackportedMethodRewriter.RewritableMethods(options, null);
+ rewritableMethods.visit(methods::add);
+ return methods;
+ }
+
public void desugar(IRCode code) {
if (rewritableMethods.isEmpty()) {
return; // Nothing to do!
@@ -230,9 +241,8 @@
// Map backported method to a provider for creating the actual target method (with code).
private final Map<DexMethod, MethodProvider> rewritable = new IdentityHashMap<>();
- public RewritableMethods(AppView<?> appView) {
- InternalOptions options = appView.options();
- DexItemFactory factory = appView.dexItemFactory();
+ public RewritableMethods(InternalOptions options, AppView<?> appView) {
+ DexItemFactory factory = options.itemFactory;
if (options.minApiLevel < AndroidApiLevel.K.getLevel()) {
initializeAndroidKMethodProviders(factory);
@@ -266,6 +276,10 @@
return rewritable.isEmpty();
}
+ public void visit(Consumer<DexMethod> consumer) {
+ rewritable.keySet().forEach(consumer);
+ }
+
private void initializeAndroidKMethodProviders(DexItemFactory factory) {
// Note: Long.compare rewriting is handled by CodeRewriter since there is a dedicated
// bytecode which supports the operation.
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index efe42a1..f8ee25b 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -695,11 +695,20 @@
return getAndroidJar(AndroidApiLevel.getAndroidApiLevel(apiLevel));
}
- public static Path getAndroidJar(AndroidApiLevel apiLevel) {
+ private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
String jar = String.format(
ANDROID_JAR_PATTERN,
(apiLevel == AndroidApiLevel.getDefault() ? DEFAULT_MIN_SDK : apiLevel).getLevel());
- Path path = Paths.get(jar);
+ return Paths.get(jar);
+ }
+
+ public static boolean hasAndroidJar(AndroidApiLevel apiLevel) {
+ Path path = getAndroidJarPath(apiLevel);
+ return Files.exists(path);
+ }
+
+ public static Path getAndroidJar(AndroidApiLevel apiLevel) {
+ Path path = getAndroidJarPath(apiLevel);
assert Files.exists(path)
: "Expected android jar to exist for API level " + apiLevel;
return path;
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
new file mode 100644
index 0000000..2c7cbe5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -0,0 +1,55 @@
+// 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.desugar.backports;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexMethod;
+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.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+
+public class TestBackportedNotPresentInAndroidJar {
+
+ @Test
+ public void testBackportedMethodsPerAPILevel() throws Exception {
+ for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
+ if (!ToolHelper.hasAndroidJar(apiLevel)) {
+ // Only check for the android jar versions present in third_party.
+ System.out.println("Skipping check for " + apiLevel);
+ continue;
+ }
+ // 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));
+ List<DexMethod> backportedMethods =
+ BackportedMethodRewriter.generateListOfBackportedMethods(apiLevel);
+ for (DexMethod method : backportedMethods) {
+ // Two different DexItemFactories are in play, but as toSourceString is used for lookup
+ // that is not an issue.
+ ClassSubject clazz = inspector.clazz(method.holder.toSourceString());
+ MethodSubject foundInAndroidJar =
+ clazz.method(
+ method.proto.returnType.toSourceString(),
+ method.name.toSourceString(),
+ Arrays.stream(method.proto.parameters.values)
+ .map(DexType::toSourceString)
+ .collect(Collectors.toList()));
+ assertThat(
+ foundInAndroidJar + " present in " + apiLevel, foundInAndroidJar, not(isPresent()));
+ }
+ }
+ }
+}