Generate jar and move running of tests to main

Bug: 190368382
Bug: 188388130
Bug: 138781768
Change-Id: Ib0ccda93bef8affff86af02aa8fd94fa8298a1a3
diff --git a/.gitignore b/.gitignore
index c9c43534..24eead1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,8 @@
 third_party/android_jar/lib.tar.gz
 third_party/android_jar/api-versions.tar.gz
 third_party/android_jar/api-versions
+third_party/android_jar/api-database.tar.gz
+third_party/android_jar/api-database
 third_party/android_sdk
 third_party/android_sdk.tar.gz
 third_party/bazel
diff --git a/build.gradle b/build.gradle
index 9032bc4..273e192 100644
--- a/build.gradle
+++ b/build.gradle
@@ -316,6 +316,7 @@
                 "android_jar/lib-v30",
                 "android_jar/lib-v31",
                 "android_jar/api-versions",
+                "android_jar/api-database",
                 "api-outlining/simple-app-dump",
                 "core-lambda-stubs",
                 "dart-sdk",
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index 4a9c41a..f33f5bd 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -4,7 +4,6 @@
 
 package com.android.tools.r8.androidapi;
 
-import com.android.tools.r8.KeepForSubclassing;
 import com.android.tools.r8.references.ClassReference;
 import com.android.tools.r8.references.FieldReference;
 import com.android.tools.r8.references.MethodReference;
@@ -17,7 +16,6 @@
 import java.util.function.BiFunction;
 
 /** This is a base class for all generated classes from api-versions.xml. */
-@KeepForSubclassing
 public abstract class AndroidApiClass {
 
   private final ClassReference classReference;
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index a85a657..1e4082b 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -3,14 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import com.android.tools.r8.Keep;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.utils.structural.Ordered;
 import java.util.Arrays;
 import java.util.List;
 
 /** Android API level description */
-@Keep
 public enum AndroidApiLevel implements Ordered<AndroidApiLevel> {
   B(1),
   B_1_1(2),
@@ -45,6 +43,8 @@
   S(31),
   UNKNOWN(10000);
 
+  // When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
+  // and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
   public static final AndroidApiLevel LATEST = S;
 
   public static final int magicApiLevelUsedByAndroidPlatformBuild = 10000;
diff --git a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
index e3b1753..df59f20 100644
--- a/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
+++ b/src/main/java/com/android/tools/r8/utils/TraversalContinuation.java
@@ -3,11 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import com.android.tools.r8.Keep;
-
 /** Two value continuation value to indicate the continuation of a loop/traversal. */
 /* This class is used for building up api class member traversals. */
-@Keep
 public enum TraversalContinuation {
   CONTINUE,
   BREAK;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
index 5e75e09..bc25cd4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -21,22 +21,19 @@
 import static org.objectweb.asm.Opcodes.POP;
 
 import com.android.tools.r8.TestBase;
-import com.android.tools.r8.androidapi.AndroidApiClass;
 import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
 import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.references.TypeReference;
 import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
 import com.android.tools.r8.transformers.MethodTransformer;
-import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.TraversalContinuation;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import org.objectweb.asm.Label;
@@ -48,6 +45,13 @@
     return descriptor(AndroidApiDatabaseBuilderTemplate.class).replace("Template", "");
   }
 
+  public static ClassReference ANDROID_API_LEVEL =
+      Reference.classFromBinaryName("com/android/tools/r8/utils/AndroidApiLevel");
+  public static ClassReference ANDROID_API_CLASS =
+      Reference.classFromBinaryName("com/android/tools/r8/androidapi/AndroidApiClass");
+  public static ClassReference TRAVERSAL_CONTINUATION =
+      Reference.classFromBinaryName("com/android/tools/r8/utils/TraversalContinuation");
+
   /**
    * Generate the classes needed for looking up api level of references in the android.jar.
    *
@@ -92,9 +96,10 @@
               .addMethodTransformer(getApiLevelTransformer(apiClass))
               .addMethodTransformer(getVisitFieldsTransformer(apiClass))
               .addMethodTransformer(getVisitMethodsTransformer(apiClass))
-              .removeMethods(MethodPredicate.onName("placeHolder"))
-              .removeMethods(MethodPredicate.onName("placeHolderForGetApiLevel"))
               .removeMethods(MethodPredicate.onName("placeHolderForInit"))
+              .removeMethods(MethodPredicate.onName("placeHolderForGetApiLevel"))
+              .removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
+              .removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
               .transform());
     }
 
@@ -114,7 +119,8 @@
             .setClassDescriptor(generatedMainDescriptor())
             .addMethodTransformer(getVisitApiClassesTransformer(apiClasses))
             .addMethodTransformer(getBuildPackageTransformer(packages))
-            .removeMethods(MethodPredicate.onName("placeHolder"))
+            .removeMethods(MethodPredicate.onName("placeHolderForVisitApiClasses"))
+            .removeMethods(MethodPredicate.onName("placeHolderForBuildClass"))
             .transform());
   }
 
@@ -150,18 +156,16 @@
   //     return placeHolderForGetApiLevel();
   // into
   //    return AndroidApiLevel.getAndroidApiLevel(<apiLevel>);
-  private static MethodTransformer getApiLevelTransformer(ParsedApiClass apiClass)
-      throws NoSuchMethodException {
-    Method getAndroidApiLevel = AndroidApiLevel.class.getMethod("getAndroidApiLevel", int.class);
+  private static MethodTransformer getApiLevelTransformer(ParsedApiClass apiClass) {
     return replaceCode(
         "placeHolderForGetApiLevel",
         transformer -> {
           transformer.visitLdcInsn(apiClass.getApiLevel().getLevel());
           transformer.visitMethodInsn(
               INVOKESTATIC,
-              binaryName(AndroidApiLevel.class),
-              getAndroidApiLevel.getName(),
-              methodDescriptor(getAndroidApiLevel),
+              ANDROID_API_LEVEL.getBinaryName(),
+              "getAndroidApiLevel",
+              "(I)" + ANDROID_API_LEVEL.getDescriptor(),
               false);
         });
   }
@@ -180,9 +184,7 @@
   //    }
   //    ...
   //    return TraversalContinuation.CONTINUE;
-  private static MethodTransformer getVisitFieldsTransformer(ParsedApiClass apiClass)
-      throws NoSuchMethodException {
-    Method shouldBreak = TraversalContinuation.class.getMethod("shouldBreak");
+  private static MethodTransformer getVisitFieldsTransformer(ParsedApiClass apiClass) {
     return replaceCode(
         "placeHolderForVisitFields",
         transformer -> {
@@ -197,22 +199,21 @@
                       transformer.visitVarInsn(ALOAD, 1);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
-                          binaryName(AndroidApiClass.class),
+                          ANDROID_API_CLASS.getBinaryName(),
                           "visitField",
-                          methodDescriptor(
-                              TraversalContinuation.class,
-                              String.class,
-                              String.class,
-                              int.class,
-                              BiFunction.class),
+                          "(Ljava/lang/String;"
+                              + "Ljava/lang/String;"
+                              + "I"
+                              + "Ljava/util/function/BiFunction;)"
+                              + TRAVERSAL_CONTINUATION.getDescriptor(),
                           false);
                       // Note that instead of storing the result here, we dup it on the stack.
                       transformer.visitInsn(DUP);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
-                          binaryName(TraversalContinuation.class),
-                          shouldBreak.getName(),
-                          methodDescriptor(shouldBreak),
+                          TRAVERSAL_CONTINUATION.getBinaryName(),
+                          "shouldBreak",
+                          "()Z",
                           false);
                       Label label = new Label();
                       transformer.visitJumpInsn(IFEQ, label);
@@ -223,7 +224,7 @@
                           0,
                           new Object[] {},
                           1,
-                          new Object[] {binaryName(TraversalContinuation.class)});
+                          new Object[] {TRAVERSAL_CONTINUATION.getBinaryName()});
                       // The pop here is needed to remove the dupped value in the case we do not
                       // return.
                       transformer.visitInsn(POP);
@@ -248,9 +249,7 @@
   //    }
   //    ...
   //    return TraversalContinuation.CONTINUE;
-  private static MethodTransformer getVisitMethodsTransformer(ParsedApiClass apiClass)
-      throws NoSuchMethodException {
-    Method shouldBreak = TraversalContinuation.class.getMethod("shouldBreak");
+  private static MethodTransformer getVisitMethodsTransformer(ParsedApiClass apiClass) {
     return replaceCode(
         "placeHolderForVisitMethods",
         transformer -> {
@@ -278,23 +277,21 @@
                       transformer.visitVarInsn(ALOAD, 1);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
-                          binaryName(AndroidApiClass.class),
+                          ANDROID_API_CLASS.getBinaryName(),
                           "visitMethod",
-                          methodDescriptor(
-                              TraversalContinuation.class,
-                              String.class,
-                              String[].class,
-                              String.class,
-                              int.class,
-                              BiFunction.class),
+                          "(Ljava/lang/String;"
+                              + "[Ljava/lang/String;Ljava/lang/String;"
+                              + "I"
+                              + "Ljava/util/function/BiFunction;)"
+                              + TRAVERSAL_CONTINUATION.getDescriptor(),
                           false);
                       // Note that instead of storing the result here, we dup it on the stack.
                       transformer.visitInsn(DUP);
                       transformer.visitMethodInsn(
                           INVOKEVIRTUAL,
-                          binaryName(TraversalContinuation.class),
-                          shouldBreak.getName(),
-                          methodDescriptor(shouldBreak),
+                          TRAVERSAL_CONTINUATION.getBinaryName(),
+                          "shouldBreak",
+                          "()Z",
                           false);
                       Label label = new Label();
                       transformer.visitJumpInsn(IFEQ, label);
@@ -305,7 +302,7 @@
                           0,
                           new Object[] {},
                           1,
-                          new Object[] {binaryName(TraversalContinuation.class)});
+                          new Object[] {TRAVERSAL_CONTINUATION.getBinaryName()});
                       // The pop here is needed to remove the dupped value in the case we do not
                       // return.
                       transformer.visitInsn(POP);
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index c47353e..c69eff3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -5,15 +5,20 @@
 package com.android.tools.r8.apimodel;
 
 import static com.android.tools.r8.apimodel.AndroidApiDatabaseBuilderGenerator.generatedMainDescriptor;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assume.assumeTrue;
 
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.JvmTestBuilder;
 import com.android.tools.r8.JvmTestRunResult;
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestState;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass;
+import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest;
 import com.android.tools.r8.references.Reference;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.BooleanBox;
@@ -23,12 +28,16 @@
 import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.TraversalContinuation;
 import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BiConsumer;
+import java.util.concurrent.ExecutionException;
+import java.util.function.BiFunction;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
@@ -41,11 +50,13 @@
   protected final TestParameters parameters;
   private static final Path API_VERSIONS_XML =
       Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-versions", "api-versions.xml");
+  private static final Path API_DATABASE_JAR =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "android_jar", "api-database", "api-database.jar");
   private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.R;
 
   @Parameters(name = "{0}")
   public static TestParametersCollection data() {
-    return getTestParameters().withSystemRuntime().build();
+    return getTestParameters().withNoneRuntime().build();
   }
 
   public AndroidApiDatabaseBuilderGeneratorTest(TestParameters parameters) {
@@ -74,15 +85,6 @@
     return builder.build();
   }
 
-  public static void main(String[] args) throws Exception {
-    generateJar();
-  }
-
-  @Test
-  public void testDatabaseGenerationUpToDate() {
-    assumeTrue("b/190368382", false);
-  }
-
   @Test
   public void testCanParseApiVersionsXml() throws Exception {
     // This tests makes a rudimentary check on the number of classes, fields and methods in
@@ -108,64 +110,115 @@
     assertEquals(38661, numberOfMethods.get());
   }
 
-  @Test()
-  public void testGeneratedOutputForVisitClasses() throws Exception {
-    runTest(
-        TestGeneratedMainVisitClasses.class,
-        (parsedApiClasses, runResult) -> {
-          String expectedOutput =
-              StringUtils.lines(
-                  ListUtils.map(
-                      parsedApiClasses, apiClass -> apiClass.getClassReference().getDescriptor()));
-          runResult.assertSuccessWithOutput(expectedOutput);
-        });
+  @Test
+  public void testDatabaseGenerationUpToDate() throws Exception {
+    BootstrapCurrentEqualityTest.filesAreEqual(generateJar(), API_DATABASE_JAR);
   }
 
-  @Test()
-  public void testBuildClassesContinue() throws Exception {
-    runTest(
-        TestBuildClassesContinue.class,
-        (parsedApiClasses, runResult) -> {
-          runResult.assertSuccessWithOutputLines(getExpected(parsedApiClasses, false));
-        });
-  }
-
-  @Test()
-  public void testBuildClassesBreak() throws Exception {
-    runTest(
-        TestBuildClassesBreak.class,
-        (parsedApiClasses, runResult) -> {
-          runResult.assertSuccessWithOutputLines(getExpected(parsedApiClasses, true));
-        });
-  }
-
-  private void runTest(
-      Class<?> testClass, BiConsumer<List<ParsedApiClass>, JvmTestRunResult> resultConsumer)
-      throws Exception {
+  /**
+   * Main entry point for building a database over references in framework to the api level they
+   * were introduced. Running main will generate a new jar and run tests on it to ensure it is
+   * compatible with R8 sources and works as expected.
+   *
+   * <p>If the generated jar passes tests it will be moved to third_party/android_jar/api-database/
+   * and override the current file in there.
+   */
+  public static void main(String[] args) throws Exception {
     List<ParsedApiClass> parsedApiClasses =
         AndroidApiVersionsXmlParser.getParsedApiClasses(API_VERSIONS_XML.toFile(), API_LEVEL);
-    testForJvm()
-        .addProgramClassFileData(
-            transformer(testClass)
-                .replaceClassDescriptorInMethodInstructions(
-                    descriptor(AndroidApiDatabaseBuilderTemplate.class), generatedMainDescriptor())
-                .transform())
-        .addLibraryFiles(generateJar(parsedApiClasses))
-        // TODO(b/190368382): This will change when databasebuilder is included in deps.
-        .addLibraryFiles(ToolHelper.R8_WITHOUT_DEPS_JAR, ToolHelper.DEPS)
-        .addDefaultRuntimeLibrary(parameters)
-        .run(parameters.getRuntime(), testClass)
-        .apply(
-            result -> {
-              if (result.getExitCode() != 0) {
-                System.out.println(result.getStdErr());
-              }
-            })
-        .assertSuccess()
-        .apply(result -> resultConsumer.accept(parsedApiClasses, result));
+    Path generatedJar = generateJar(parsedApiClasses);
+    validateJar(generatedJar, parsedApiClasses);
+    Files.move(generatedJar, API_DATABASE_JAR, REPLACE_EXISTING);
   }
 
-  private List<String> getExpected(List<ParsedApiClass> parsedApiClasses, boolean abort) {
+  private static void validateJar(Path generated, List<ParsedApiClass> apiClasses) {
+    List<BiFunction<Path, List<ParsedApiClass>, Boolean>> tests =
+        ImmutableList.of(
+            AndroidApiDatabaseBuilderGeneratorTest::testGeneratedOutputForVisitClasses,
+            AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesContinue,
+            AndroidApiDatabaseBuilderGeneratorTest::testBuildClassesBreak,
+            AndroidApiDatabaseBuilderGeneratorTest::testNoPlaceHolder);
+    tests.forEach(
+        test -> {
+          if (!test.apply(generated, apiClasses)) {
+            throw new RuntimeException("Generated jar did not pass tests");
+          }
+        });
+  }
+
+  private static boolean testGeneratedOutputForVisitClasses(
+      Path generated, List<ParsedApiClass> parsedApiClasses) {
+    String expectedOutput =
+        StringUtils.lines(
+            ListUtils.map(
+                parsedApiClasses, apiClass -> apiClass.getClassReference().getDescriptor()));
+    return runTest(generated, TestGeneratedMainVisitClasses.class)
+        .getStdOut()
+        .equals(expectedOutput);
+  }
+
+  private static boolean testBuildClassesContinue(
+      Path generated, List<ParsedApiClass> parsedApiClasses) {
+    return runTest(generated, TestBuildClassesContinue.class)
+        .getStdOut()
+        .equals(getExpected(parsedApiClasses, false));
+  }
+
+  private static boolean testBuildClassesBreak(
+      Path generated, List<ParsedApiClass> parsedApiClasses) {
+    return runTest(generated, TestBuildClassesBreak.class)
+        .getStdOut()
+        .equals(getExpected(parsedApiClasses, true));
+  }
+
+  private static boolean testNoPlaceHolder(Path generated, List<ParsedApiClass> parsedApiClasses) {
+    try {
+      CodeInspector inspector = new CodeInspector(generated);
+      inspector
+          .allClasses()
+          .forEach(
+              clazz -> {
+                clazz.forAllMethods(
+                    methods -> {
+                      if (methods.getFinalName().startsWith("placeHolder")) {
+                        throw new RuntimeException("Found placeHolder method in generated jar");
+                      }
+                    });
+              });
+    } catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+    return true;
+  }
+
+  private static JvmTestRunResult runTest(Path generated, Class<?> testClass) {
+    try {
+      TemporaryFolder temporaryFolder = new TemporaryFolder();
+      temporaryFolder.create();
+      return JvmTestBuilder.create(new TestState(temporaryFolder))
+          .addProgramClassFileData(
+              transformer(testClass)
+                  .replaceClassDescriptorInMethodInstructions(
+                      descriptor(AndroidApiDatabaseBuilderTemplate.class),
+                      generatedMainDescriptor())
+                  .transform())
+          .addLibraryFiles(generated)
+          // TODO(b/190368382): This will change when databasebuilder is included in deps.
+          .addLibraryFiles(ToolHelper.R8_WITHOUT_DEPS_JAR, ToolHelper.DEPS)
+          .addLibraryFiles(ToolHelper.getJava8RuntimeJar())
+          .run(TestRuntime.getSystemRuntime(), testClass)
+          .apply(
+              result -> {
+                if (result.getExitCode() != 0) {
+                  throw new RuntimeException(result.getStdErr());
+                }
+              });
+    } catch (IOException | ExecutionException | CompilationFailedException ex) {
+      throw new RuntimeException(ex);
+    }
+  }
+
+  private static String getExpected(List<ParsedApiClass> parsedApiClasses, boolean abort) {
     List<String> expected = new ArrayList<>();
     parsedApiClasses.forEach(
         apiClass -> {
@@ -200,7 +253,7 @@
                     });
               });
         });
-    return expected;
+    return StringUtils.lines(expected);
   }
 
   public static class TestGeneratedMainVisitClasses {
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
new file mode 100644
index 0000000..84abf49
--- /dev/null
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -0,0 +1 @@
+e4da4b29079ac393e0012e7676dcca0799841e29
\ No newline at end of file