Add API and change default for missing library API modeling.

Bug: b/231547906
Change-Id: Iafc7031557535bc5af6cfa53b3d2b48006a1ab85
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index c953853..33e984b 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -93,6 +93,7 @@
     private boolean minimalMainDex = false;
     private boolean skipDump = false;
     private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
+    private boolean enableMissingLibraryApiModeling = false;
 
     private Builder() {
       this(new DefaultD8DiagnosticsHandler());
@@ -288,6 +289,18 @@
       return self();
     }
 
+    /**
+     * Enable experimental/pre-release support for modeling missing library APIs.
+     *
+     * <p>This allows enabling the feature while it is still default disabled by the compiler. Once
+     * the feature is default enabled, calling this method will have no affect.
+     */
+    @Deprecated
+    public Builder setEnableExperimentalMissingLibraryApiModeling(boolean enable) {
+      this.enableMissingLibraryApiModeling = enable;
+      return self();
+    }
+
     @Override
     void validate() {
       if (isPrintHelp()) {
@@ -376,6 +389,7 @@
           getThreadCount(),
           getDumpInputFlags(),
           getMapIdProvider(),
+          enableMissingLibraryApiModeling,
           factory);
     }
   }
@@ -392,6 +406,7 @@
   private final boolean enableMainDexListCheck;
   private final boolean minimalMainDex;
   private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
+  private final boolean enableMissingLibraryApiModeling;
   private final DexItemFactory factory;
 
   public static Builder builder() {
@@ -460,6 +475,7 @@
       int threadCount,
       DumpInputFlags dumpInputFlags,
       MapIdProvider mapIdProvider,
+      boolean enableMissingLibraryApiModeling,
       DexItemFactory factory) {
     super(
         inputApp,
@@ -488,6 +504,7 @@
     this.enableMainDexListCheck = enableMainDexListCheck;
     this.minimalMainDex = minimalMainDex;
     this.mainDexKeepRules = mainDexKeepRules;
+    this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
     this.factory = factory;
   }
 
@@ -503,6 +520,7 @@
     enableMainDexListCheck = true;
     minimalMainDex = false;
     mainDexKeepRules = null;
+    enableMissingLibraryApiModeling = false;
     factory = null;
   }
 
@@ -556,6 +574,11 @@
     internal.synthesizedClassPrefix = synthesizedClassPrefix;
     internal.desugaredLibraryKeepRuleConsumer = desugaredLibraryKeepRuleConsumer;
 
+    if (!enableMissingLibraryApiModeling) {
+      internal.apiModelingOptions().disableApiCallerIdentification();
+      internal.apiModelingOptions().disableMissingApiModeling();
+    }
+
     // Default is to remove all javac generated assertion code when generating dex.
     assert internal.assertionsConfiguration == null;
     internal.assertionsConfiguration =
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index f4bb9c2..ee941db 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -216,6 +216,8 @@
 
     // Disable global optimizations.
     internal.disableGlobalOptimizations();
+    internal.apiModelingOptions().disableApiCallerIdentification();
+    internal.apiModelingOptions().disableMissingApiModeling();
 
     internal.setDumpInputFlags(getDumpInputFlags(), false);
     internal.dumpOptions = dumpOptions();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index f259f26..b834389 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -115,6 +115,7 @@
     private final List<FeatureSplit> featureSplits = new ArrayList<>();
     private String synthesizedClassPrefix = "";
     private boolean skipDump = false;
+    private boolean enableMissingLibraryApiModeling = false;
 
     private boolean allowTestProguardOptions =
         System.getProperty("com.android.tools.r8.allowTestProguardOptions") != null;
@@ -432,6 +433,18 @@
       return self();
     }
 
+    /**
+     * Enable experimental/pre-release support for modeling missing library APIs.
+     *
+     * <p>This allows enabling the feature while it is still default disabled by the compiler. Once
+     * the feature is default enabled, calling this method will have no affect.
+     */
+    @Deprecated
+    public Builder setEnableExperimentalMissingLibraryApiModeling(boolean enable) {
+      this.enableMissingLibraryApiModeling = enable;
+      return self();
+    }
+
     @Override
     protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
         Path path,
@@ -620,7 +633,8 @@
               getThreadCount(),
               getDumpInputFlags(),
               getMapIdProvider(),
-              getSourceFileProvider());
+              getSourceFileProvider(),
+              enableMissingLibraryApiModeling);
 
       if (inputDependencyGraphConsumer != null) {
         inputDependencyGraphConsumer.finished();
@@ -703,6 +717,7 @@
   private final FeatureSplitConfiguration featureSplitConfiguration;
   private final String synthesizedClassPrefix;
   private final boolean skipDump;
+  private final boolean enableMissingLibraryApiModeling;
 
   /** Get a new {@link R8Command.Builder}. */
   public static Builder builder() {
@@ -786,7 +801,8 @@
       int threadCount,
       DumpInputFlags dumpInputFlags,
       MapIdProvider mapIdProvider,
-      SourceFileProvider sourceFileProvider) {
+      SourceFileProvider sourceFileProvider,
+      boolean enableMissingLibraryApiModeling) {
     super(
         inputApp,
         mode,
@@ -825,6 +841,7 @@
     this.featureSplitConfiguration = featureSplitConfiguration;
     this.synthesizedClassPrefix = synthesizedClassPrefix;
     this.skipDump = skipDump;
+    this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -848,6 +865,7 @@
     featureSplitConfiguration = null;
     synthesizedClassPrefix = null;
     skipDump = false;
+    enableMissingLibraryApiModeling = false;
   }
 
   public DexItemFactory getDexItemFactory() {
@@ -952,6 +970,10 @@
 
     internal.outputInspections = InspectorImpl.wrapInspections(getOutputInspections());
 
+    if (!enableMissingLibraryApiModeling) {
+      internal.apiModelingOptions().disableMissingApiModeling();
+    }
+
     // Default is to remove all javac generated assertion code when generating dex.
     assert internal.assertionsConfiguration == null;
     internal.assertionsConfiguration =
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 1545d7c..d0269ec 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -257,7 +257,6 @@
     enableInitializedClassesAnalysis = false;
     callSiteOptimizationOptions.disableOptimization();
     horizontalClassMergerOptions.setRestrictToSynthetics();
-    apiModelTestingOptions.disableApiCallerIdentification();
   }
 
   public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
@@ -1653,6 +1652,15 @@
           });
     }
 
+    /**
+     * Disable the workarounds for missing APIs. This does not disable the use of the database, just
+     * the introduction of soft-verification workarounds for potentially missing API references.
+     */
+    public void disableMissingApiModeling() {
+      enableOutliningOfMethods = false;
+      enableStubbingOfClasses = false;
+    }
+
     public void disableApiCallerIdentification() {
       enableApiCallerIdentification = false;
     }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
index 784fce6..cef795c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -69,7 +69,7 @@
         .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkOutput)
-        .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+        .inspect(this::inspect);
   }
 
   @Test
@@ -80,8 +80,6 @@
             || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForD8()
         .setMode(CompilationMode.RELEASE)
-        // TODO(b/213552119): Remove when enabled by default.
-        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(this::setupTestBuilder)
         .compile()
         .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
index 8651a23..a264c9e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -62,7 +62,7 @@
         .setMode(CompilationMode.DEBUG)
         .apply(this::setupTestBuilder)
         .compile()
-        .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+        .inspect(this::inspect)
         .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkOutput);
@@ -76,8 +76,6 @@
             || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForD8()
         .setMode(CompilationMode.RELEASE)
-        // TODO(b/213552119): Remove when enabled by default.
-        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(this::setupTestBuilder)
         .compile()
         .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index 1397da7..50e1179 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -70,10 +70,10 @@
         .setMode(CompilationMode.DEBUG)
         .apply(this::setupTestBuilder)
         .compile()
-        .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
-        .apply(this::checkOutput);
+        .apply(this::checkOutput)
+        .inspect(this::inspect);
   }
 
   @Test
@@ -84,8 +84,6 @@
             || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForD8()
         .setMode(CompilationMode.RELEASE)
-        // TODO(b/213552119): Remove when enabled by default.
-        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(this::setupTestBuilder)
         .compile()
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index 3d6d30c..c45df55 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -69,10 +69,10 @@
         .setMode(CompilationMode.DEBUG)
         .apply(this::setupTestBuilder)
         .compile()
-        .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
-        .apply(this::checkOutput);
+        .apply(this::checkOutput)
+        .inspect(this::inspect);
   }
 
   @Test
@@ -83,8 +83,6 @@
             || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForD8()
         .setMode(CompilationMode.RELEASE)
-        // TODO(b/213552119): Remove when enabled by default.
-        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(this::setupTestBuilder)
         .compile()
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index c2a6d4d..c7ba86d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -79,7 +79,6 @@
         .setMode(CompilationMode.DEBUG)
         .apply(this::setupTestBuilder)
         .compile()
-        .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
         .applyIf(
             addLibraryClassesToBootClasspath(),
             b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
@@ -87,7 +86,8 @@
             addOtherLibraryClassesToBootClasspath(),
             b -> b.addBootClasspathClasses(OtherLibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
-        .apply(this::checkOutput);
+        .apply(this::checkOutput)
+        .inspect(this::inspect);
   }
 
   @Test
@@ -98,8 +98,6 @@
             || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V12_0_0));
     testForD8()
         .setMode(CompilationMode.RELEASE)
-        // TODO(b/213552119): Remove when enabled by default.
-        .apply(ApiModelingTestHelper::enableApiCallerIdentification)
         .apply(this::setupTestBuilder)
         .compile()
         .applyIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index 85429ea..03f9ec5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -9,7 +9,6 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
@@ -84,8 +83,7 @@
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .assertSuccessWithOutput(result)
-        // TODO(b/213552119): Add stubs to D8
-        .inspect(inspector -> inspect(inspector, false));
+        .inspect(this::inspect);
   }
 
   @Test
@@ -103,7 +101,7 @@
         .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
         .run(parameters.getRuntime(), Main.class)
         .apply(this::checkOutput)
-        .inspect(inspector -> inspect(inspector, true));
+        .inspect(this::inspect);
   }
 
   private void checkOutput(SingleTestRunResult<?> runResult) {
@@ -115,16 +113,12 @@
     }
   }
 
-  private void inspect(CodeInspector inspector, boolean canStub) throws Exception {
+  private void inspect(CodeInspector inspector) throws Exception {
     Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
     Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
     assertThat(inspector.method(mainMethod), isPresent());
     verifyThat(inspector, parameters, methodOn23).isNotOutlinedFrom(mainMethod);
-    if (canStub) {
-      verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel);
-    } else {
-      assertThat(inspector.clazz(LibraryClass.class), not(isPresent()));
-    }
+    verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(libraryApiLevel);
   }
 
   // Only present from api level 23.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 40f9e10..62a1aab 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -116,6 +116,7 @@
   static void enableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
     compilerBuilder.addOptionsModification(
         options -> {
+          options.apiModelingOptions().enableApiCallerIdentification = true;
           options.apiModelingOptions().enableStubbingOfClasses = true;
         });
   }
@@ -123,6 +124,7 @@
   static void enableOutliningOfMethods(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
     compilerBuilder.addOptionsModification(
         options -> {
+          options.apiModelingOptions().enableApiCallerIdentification = true;
           options.apiModelingOptions().enableOutliningOfMethods = true;
         });
   }
@@ -131,6 +133,7 @@
       TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
     compilerBuilder.addOptionsModification(
         options -> {
+          options.apiModelingOptions().enableApiCallerIdentification = true;
           options.apiModelingOptions().checkAllApiReferencesAreSet = false;
         });
   }
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 501370c..905947d 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.compilerapi.mockdata.MockClassWithAssertion;
 import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
 import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
+import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -40,7 +41,8 @@
           DesugarDependenciesTest.ApiTest.class);
 
   private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
-      ImmutableList.of(GlobalSyntheticsTest.ApiTest.class);
+      ImmutableList.of(
+          GlobalSyntheticsTest.ApiTest.class, EnableMissingLibraryApiModelingTest.ApiTest.class);
 
   private final TemporaryFolder temp;
 
diff --git a/src/test/java/com/android/tools/r8/compilerapi/wrappers/EnableMissingLibraryApiModelingTest.java b/src/test/java/com/android/tools/r8/compilerapi/wrappers/EnableMissingLibraryApiModelingTest.java
new file mode 100644
index 0000000..115212d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/compilerapi/wrappers/EnableMissingLibraryApiModelingTest.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, 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.compilerapi.wrappers;
+
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import org.junit.Test;
+
+public class EnableMissingLibraryApiModelingTest extends CompilerApiTestRunner {
+
+  public EnableMissingLibraryApiModelingTest(TestParameters parameters) {
+    super(parameters);
+  }
+
+  @Override
+  public Class<? extends CompilerApiTest> binaryTestClass() {
+    return ApiTest.class;
+  }
+
+  @Test
+  public void test() throws Exception {
+    new ApiTest(ApiTest.PARAMETERS).run();
+  }
+
+  public static class ApiTest extends CompilerApiTest {
+
+    public ApiTest(Object parameters) {
+      super(parameters);
+    }
+
+    public void run() throws Exception {
+      D8Command.builder().setEnableExperimentalMissingLibraryApiModeling(true);
+      R8Command.builder().setEnableExperimentalMissingLibraryApiModeling(true);
+    }
+
+    @Test
+    public void test() throws Exception {
+      run();
+    }
+  }
+}