[ApiModel] Disable stubbing of super classes on L and below
Bug: b/279780940
Change-Id: I1afd04dcb2134815162f073e186d25d2c61f6fca
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index c42f8c6..27a43b9 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.WorkList;
@@ -116,9 +117,13 @@
// We cannot reliably create a stub that will have the same throwing behavior for all VMs.
// Only create stubs for exceptions to allow them being present in catch handlers and super
// types of existing program classes. See b/258270051 and b/259076765 for more information.
- clazz
- .allImmediateSupertypes()
- .forEach(superType -> findReferencedLibraryClasses(superType, clazz));
+ // Also, for L devices we can have verification issues if there are super invokes to missing
+ // members on stubbed classes. See b/279780940 for more information.
+ if (appView.options().getMinApiLevel().isGreaterThan(AndroidApiLevel.L)) {
+ clazz
+ .allImmediateSupertypes()
+ .forEach(superType -> findReferencedLibraryClasses(superType, clazz));
+ }
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
index 2332313..3e60454 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
@@ -82,7 +82,8 @@
}
private boolean willStubLibraryClassThree() {
- return parameters.getApiLevel().isLessThan(mockApiLevelThree);
+ return parameters.getApiLevel().isGreaterThan(AndroidApiLevel.L)
+ && parameters.getApiLevel().isLessThan(mockApiLevelThree);
}
public AndroidApiLevel getApiLevelForRuntime() {
@@ -222,7 +223,9 @@
// classes. Depending on the api a number of synthetic classes.
int numberOfClasses =
4
- + (willStubLibraryClassThree() ? 2 : 0)
+ + (willStubLibraryClassThree()
+ ? 2
+ : (BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelThree))))
+ BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelTwo))
+ BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelOne));
assertEquals(numberOfClasses, inspector.allClasses().size());
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 c7a2674..f03be0a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -97,7 +97,8 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
index 9394280..23945e1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
@@ -60,10 +60,15 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (!addToBootClasspath()) {
+ runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ return;
+ }
StackTraceLine clinitFrame =
StackTraceLine.builder()
.setClassName(typeName(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 3d31170..4c4590a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -121,9 +121,12 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, LibraryInterface.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, lowerMockApiLevel);
+ verifyThat(inspector, parameters, LibraryInterface.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, lowerMockApiLevel);
+ verifyThat(inspector, parameters, OtherLibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockApiLevel);
}
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 cfa2e8a..c632b1a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -277,6 +277,16 @@
|| parameters.getApiLevel().isGreaterThanOrEqualTo(finalApiLevel)));
}
+ public void stubbedBetween(AndroidApiLevel startingApilevel, AndroidApiLevel endingApiLevel) {
+ assertThat(
+ inspector.clazz(classOfInterest),
+ notIf(
+ isPresent(),
+ parameters.isCfRuntime()
+ || parameters.getApiLevel().isLessThan(startingApilevel)
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(endingApiLevel)));
+ }
+
void hasCheckCastOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
hasCheckCastOutlinedFrom(method);