[ApiModel] Enable stubbing in D8
Bug: 213552119
Change-Id: I399fd40edbbd34234abcea191013c0a5c9dc7d64
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index b54e34d..217a787 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.AssertionUtils.forTesting;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+import com.android.tools.r8.androidapi.ApiReferenceStubber;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
@@ -265,7 +266,7 @@
namingLens = RecordRewritingNamingLens.createRecordRewritingNamingLens(appView, namingLens);
if (options.isGeneratingClassFiles()) {
- finalizeApplication(inputApp, appView, executor, namingLens);
+ finalizeApplication(appView, executor);
new CfApplicationWriter(appView, marker, GraphLens.getIdentityLens(), namingLens)
.write(options.getClassFileConsumer(), inputApp);
} else {
@@ -308,7 +309,12 @@
executor, appView.appInfo().app(), appView.appInfo().getMainDexInfo());
appView.setAppInfo(appView.appInfo().rebuildWithMainDexInfo(mainDexInfo));
}
- finalizeApplication(inputApp, appView, executor, namingLens);
+
+ finalizeApplication(appView, executor);
+
+ if (options.apiModelingOptions().enableStubbingOfClasses && !appView.options().debug) {
+ new ApiReferenceStubber(appView).run(executor);
+ }
new ApplicationWriter(
appView,
@@ -330,11 +336,7 @@
}
}
- private static void finalizeApplication(
- AndroidApp inputApp,
- AppView<AppInfo> appView,
- ExecutorService executorService,
- NamingLens namingLens)
+ private static void finalizeApplication(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
SyntheticFinalization.finalize(appView, executorService);
}
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 ef86f73..e0852c5 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.androidapi;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
import com.android.tools.r8.graph.DexClass;
@@ -106,13 +106,13 @@
}
}
- private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final AppView<?> appView;
private final Map<DexLibraryClass, Set<DexMethod>> libraryClassesToMock =
new ConcurrentHashMap<>();
private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
private final AndroidApiLevelCompute apiLevelCompute;
- public ApiReferenceStubber(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ public ApiReferenceStubber(AppView<?> appView) {
this.appView = appView;
apiLevelCompute = appView.apiLevelCompute();
}
@@ -138,10 +138,18 @@
AppView<AppInfoWithLiveness> appInfoWithLivenessAppView = appView.withLiveness();
appInfoWithLivenessAppView.setAppInfo(
appInfoWithLivenessAppView.appInfo().rebuildWithLiveness(committedItems));
- } else {
+ } else if (appView.hasClassHierarchy()) {
appView
.withClassHierarchy()
- .setAppInfo(appView.appInfo().rebuildWithClassHierarchy(committedItems));
+ .setAppInfo(
+ appView.appInfo().withClassHierarchy().rebuildWithClassHierarchy(committedItems));
+ } else {
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
}
}
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 0462290..784fce6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -12,6 +12,7 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
@@ -20,6 +21,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -55,22 +57,40 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeTrue(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForD8()
+ .setMode(CompilationMode.DEBUG)
.apply(this::setupTestBuilder)
.compile()
.applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- // TODO(b/213552119): Add support for stubbing
.inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
}
@Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || 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))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
+ }
+
+ @Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
@@ -84,9 +104,7 @@
.applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .inspect(this::inspect);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
@@ -102,6 +120,10 @@
result -> result.assertStderrMatches(not(containsString("This dex file is invalid"))));
}
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
// Only present from api level 23.
public static class LibraryClass {
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 6cf2ea6..8651a23 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -6,9 +6,11 @@
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.verifyThat;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
@@ -16,6 +18,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -50,15 +53,15 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeTrue(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForD8()
+ .setMode(CompilationMode.DEBUG)
.apply(this::setupTestBuilder)
.compile()
- // TODO(b/213552119): Add support for stubbing
.inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
.applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
@@ -66,6 +69,24 @@
}
@Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || 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)
+ .applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
public void testR8() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeFalse(
@@ -75,11 +96,16 @@
.apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
.compile()
+ .inspect(this::inspect)
.applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput);
}
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
private void checkOutput(SingleTestRunResult<?> runResult) {
runResult.assertSuccessWithOutputLinesIf(isGreaterOrEqualToMockLevel(), "Hello World");
runResult.assertFailureWithErrorThatThrowsIf(
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 8d6f816..1397da7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
@@ -19,6 +20,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -59,19 +61,37 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeTrue(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForD8()
+ .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);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || 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))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- // TODO(b/213552119): Add support for stubbing
- .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ .inspect(this::inspect);
}
@Test
@@ -88,9 +108,11 @@
.applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
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 8209ef0..3d6d30c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
@@ -18,6 +19,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -58,19 +60,37 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeTrue(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForD8()
+ .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);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || 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))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- // TODO(b/213552119): Add support for stubbing
- .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ .inspect(this::inspect);
}
@Test
@@ -87,9 +107,11 @@
.applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- .inspect(
- inspector ->
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel));
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
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 cb9a7aa..c2a6d4d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestCompilerBuilder;
@@ -18,6 +19,7 @@
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.testing.AndroidBuildVersion;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -68,12 +70,36 @@
}
@Test
- public void testD8() throws Exception {
+ public void testD8Debug() throws Exception {
// TODO(b/197078995): Make this work on 12+.
assumeTrue(
parameters.isDexRuntime()
&& parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0));
testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
+ .applyIf(
+ addLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
+ .applyIf(
+ addOtherLibraryClassesToBootClasspath(),
+ b -> b.addBootClasspathClasses(OtherLibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ // TODO(b/197078995): Make this work on 12+.
+ assumeFalse(
+ parameters.isCfRuntime()
+ || 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(
@@ -84,8 +110,7 @@
b -> b.addBootClasspathClasses(OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- // TODO(b/213552119): Add support for stubbing
- .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ .inspect(this::inspect);
}
@Test
@@ -106,14 +131,13 @@
addOtherLibraryClassesToBootClasspath(),
b -> b.addBootClasspathClasses(OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkOutput)
- .inspect(
- inspector -> {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, LibraryInterface.class)
- .stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
- });
+ .inspect(this::inspect);
+ }
+
+ 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);
}
private void checkOutput(SingleTestRunResult<?> runResult) {