[ApiModel] Disable instance outlining on dalvik

Bug: b/269097876
Change-Id: I4df9ee7fd204a479f8e2d2212dd2c7f731f80e35
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 16ad20d..72d5998 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -96,6 +96,7 @@
 import com.android.tools.r8.shaking.KeepMethodInfo;
 import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
 import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.ExceptionUtils;
@@ -247,7 +248,8 @@
             ? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
             : null;
     if (appView.options().desugarState.isOn()
-        && appView.options().apiModelingOptions().enableOutliningOfMethods) {
+        && appView.options().apiModelingOptions().enableOutliningOfMethods
+        && appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
       this.instanceInitializerOutliner = new InstanceInitializerOutliner(appView);
     } else {
       this.instanceInitializerOutliner = null;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
index 51dc157..f2a6859 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
@@ -7,9 +7,8 @@
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
 import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
-import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticApiOutlineClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
 import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assume.assumeTrue;
 
@@ -23,6 +22,7 @@
 import com.android.tools.r8.testing.AndroidBuildVersion;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.reflect.Method;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -141,14 +141,12 @@
         .apply(result -> checkOutput(result, true));
   }
 
-  private void inspect(CodeInspector inspector) {
+  private void inspect(CodeInspector inspector) throws Exception {
     assertThat(inspector.clazz(Main.class), isPresent());
     assertThat(inspector.clazz(Helper.class), isPresent());
-    if (!parameters.isCfRuntime()) {
-      assertThat(
-          inspector.clazz(syntheticApiOutlineClass(Helper.class, 0)),
-          notIf(isPresent(), parameters.getApiLevel().isGreaterThanOrEqualTo(mockApiLevel)));
-    }
+    Method getLibraryClass = Helper.class.getMethod("setLibraryClass");
+    verifyThat(inspector, parameters, SubLibraryClass.class.getConstructor())
+        .isOutlinedFromBetween(getLibraryClass, AndroidApiLevel.L, mockApiLevel);
   }
 
   private void checkOutput(SingleTestRunResult<?> runResult, boolean apiModelingEnabled) {
@@ -156,9 +154,6 @@
       runResult.assertFailureWithErrorThatThrows(ClassNotFoundException.class);
     } else if (getMaxSupportedApiLevel().isGreaterThanOrEqualTo(mockApiLevel)) {
       runResult.assertSuccessWithOutputLines("SubLibraryClass::foo");
-    } else if (parameters.getDexRuntimeVersion().isDalvik() && apiModelingEnabled) {
-      // TODO(b/b/269097876): We should not cause a verification error.
-      runResult.assertFailureWithErrorThatThrows(VerifyError.class);
     } else {
       runResult.assertSuccessWithOutputLines("LibraryClass::foo");
     }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 978d068..7646f63 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -100,7 +100,9 @@
         .inspect(
             inspector -> {
               // TODO(b/187675788): Update when horizontal merging is enabled for D8 for debug mode.
-              if (parameters.getApiLevel().isLessThan(libraryClassApiLevel)) {
+              if (parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
+                assertEquals(7, inspector.allClasses().size());
+              } else if (parameters.getApiLevel().isLessThan(libraryClassApiLevel)) {
                 // We have generated 4 outlines two having api level 23 and two having api level 27
                 // and 2 outlines for each instance initializer.
                 assertEquals(11, inspector.allClasses().size());
@@ -172,9 +174,10 @@
       // If less than the library api level then we have synthesized two instance initializer
       // outlines as well.
       // Check that the levels are horizontally merged.
-      assertEquals(
-          parameters.getApiLevel().isLessThan(libraryClassApiLevel) ? 6 : 5,
-          inspector.allClasses().size());
+      boolean willOutlineInitializers =
+          parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)
+              && parameters.getApiLevel().isLessThan(libraryClassApiLevel);
+      assertEquals(willOutlineInitializers ? 6 : 5, inspector.allClasses().size());
       assertEquals(2, outlinedAddedOn23.size());
       assertTrue(
           outlinedAddedOn23.stream()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
index a362bd3..ed4b250 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
@@ -108,8 +108,9 @@
             ProgramExtendsLibraryClass.class.getMethod("print"),
             isR8 ? AndroidApiLevel.B : classApiLevel);
     verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(String.class))
-        .isOutlinedFromUntil(
+        .isOutlinedFromBetween(
             ProgramExtendsLibraryClass.class.getDeclaredConstructor(String.class),
+            AndroidApiLevel.L,
             isR8 ? AndroidApiLevel.B : classApiLevel);
   }
 
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
index b0a5fb7..0d16797 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
@@ -113,11 +113,13 @@
   private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
     Method mainMethod = Main.class.getMethod("main", String[].class);
     verifyThat(inspector, parameters, Argument.class.getDeclaredConstructor(String.class))
-        .isOutlinedFromUntil(mainMethod, classApiLevel);
+        .isOutlinedFromBetween(mainMethod, AndroidApiLevel.L, classApiLevel);
     verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(Argument.class))
-        .isOutlinedFromUntil(mainMethod, classApiLevel);
+        .isOutlinedFromBetween(mainMethod, AndroidApiLevel.L, classApiLevel);
+    // For R8 we inline into the method with an instance initializer when we do not outline it.
     verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
-        .isOutlinedFromUntil(mainMethod, classApiLevel);
+        .isOutlinedFromBetween(
+            mainMethod, isR8 ? AndroidApiLevel.L : AndroidApiLevel.B, classApiLevel);
   }
 
   private void checkOutput(SingleTestRunResult<?> runResult) {
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 e437943..6a487a9 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -447,6 +447,17 @@
       }
     }
 
+    void isOutlinedFromBetween(
+        Executable method, AndroidApiLevel lowerBoundInclusive, AndroidApiLevel upperBound) {
+      if (parameters.isDexRuntime()
+          && parameters.getApiLevel().isLessThan(upperBound)
+          && parameters.getApiLevel().isGreaterThanOrEqualTo(lowerBoundInclusive)) {
+        isOutlinedFrom(method);
+      } else {
+        isNotOutlinedFrom(method);
+      }
+    }
+
     void isOutlinedFrom(Executable method) {
       // Check that the call is in a synthetic class.
       List<FoundMethodSubject> outlinedMethod =