[ApiModel] Throw NoClassDefFoundError for mocked classes in clinit
Bug: 209803983
Bug: 138781768
Change-Id: I055ab49d8960e05e8daa1adcf3358b2035c209ed
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 6329ea5..5071174 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ThrowExceptionCode;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -134,7 +135,12 @@
if (libraryClassesToMock.isEmpty()) {
return;
}
- libraryClassesToMock.forEach(this::mockMissingLibraryClass);
+ libraryClassesToMock.forEach(
+ (clazz, methods) ->
+ mockMissingLibraryClass(
+ clazz,
+ methods,
+ ThrowExceptionCode.create(appView.dexItemFactory().noClassDefFoundErrorType)));
// Commit the synthetic items.
CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
if (appView.hasLiveness()) {
@@ -207,7 +213,10 @@
}
}
- private void mockMissingLibraryClass(DexLibraryClass libraryClass, Set<DexMethod> methodsToStub) {
+ private void mockMissingLibraryClass(
+ DexLibraryClass libraryClass,
+ Set<DexMethod> methodsToStub,
+ ThrowExceptionCode throwExceptionCode) {
if (libraryClass.getType() == appView.dexItemFactory().objectType
|| libraryClass.getType().toDescriptorString().startsWith("Ljava/")) {
return;
@@ -237,12 +246,21 @@
if (!libraryClass.isFinal()) {
classBuilder.unsetFinal();
}
- if (!libraryClass.isInterface()
- || appView.options().canUseDefaultAndStaticInterfaceMethods()) {
- classBuilder.setDirectMethods(
- buildLibraryMethodsForProgram(
- libraryClass, libraryClass.directMethods(), methodsToStub));
- }
+ List<DexEncodedMethod> directMethods =
+ (!libraryClass.isInterface()
+ || appView.options().canUseDefaultAndStaticInterfaceMethods())
+ ? buildLibraryMethodsForProgram(
+ libraryClass, libraryClass.directMethods(), methodsToStub)
+ : new ArrayList<>();
+ // Add throwing static initializer
+ directMethods.add(
+ DexEncodedMethod.syntheticBuilder()
+ .setMethod(
+ appView.dexItemFactory().createClassInitializer(libraryClass.getType()))
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setCode(throwExceptionCode)
+ .build());
+ classBuilder.setDirectMethods(directMethods);
});
}
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 4db820e..ad0961d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -51,11 +51,6 @@
.compile()
.applyIf(isLibraryOnBootClassPath, b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), NoClassDefFoundError.class)
- // TODO(b/209803983): Should fail with a NoClassDefFoundError unless the api level of the
- // runtime >= 23.
- .assertFailureWithErrorThatThrowsIf(
- parameters.isDexRuntime() && !isLibraryOnBootClassPath, UnsatisfiedLinkError.class)
.assertSuccessWithOutputLinesIf(isLibraryOnBootClassPath, "Hello World");
}