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 c158fc8..1495071 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -92,6 +92,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());
@@ -322,6 +323,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()) {
@@ -411,6 +424,7 @@
getDumpInputFlags(),
getMapIdProvider(),
proguardMapConsumer,
+ enableMissingLibraryApiModeling,
factory);
}
}
@@ -426,6 +440,7 @@
private final boolean minimalMainDex;
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
private final StringConsumer proguardMapConsumer;
+ private final boolean enableMissingLibraryApiModeling;
private final DexItemFactory factory;
public static Builder builder() {
@@ -500,6 +515,7 @@
DumpInputFlags dumpInputFlags,
MapIdProvider mapIdProvider,
StringConsumer proguardMapConsumer,
+ boolean enableMissingLibraryApiModeling,
DexItemFactory factory) {
super(
inputApp,
@@ -529,6 +545,7 @@
this.minimalMainDex = minimalMainDex;
this.mainDexKeepRules = mainDexKeepRules;
this.proguardMapConsumer = proguardMapConsumer;
+ this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
this.factory = factory;
}
@@ -545,6 +562,7 @@
minimalMainDex = false;
mainDexKeepRules = null;
proguardMapConsumer = null;
+ enableMissingLibraryApiModeling = false;
factory = null;
}
@@ -602,6 +620,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 6c8f251..2b50253 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -213,6 +213,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 4e3e190..4e7ca32 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -114,6 +114,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;
@@ -431,6 +432,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,
@@ -619,7 +632,8 @@
getThreadCount(),
getDumpInputFlags(),
getMapIdProvider(),
- getSourceFileProvider());
+ getSourceFileProvider(),
+ enableMissingLibraryApiModeling);
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -704,6 +718,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() {
@@ -792,7 +807,8 @@
int threadCount,
DumpInputFlags dumpInputFlags,
MapIdProvider mapIdProvider,
- SourceFileProvider sourceFileProvider) {
+ SourceFileProvider sourceFileProvider,
+ boolean enableMissingLibraryApiModeling) {
super(
inputApp,
mode,
@@ -831,6 +847,7 @@
this.featureSplitConfiguration = featureSplitConfiguration;
this.synthesizedClassPrefix = synthesizedClassPrefix;
this.skipDump = skipDump;
+ this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
}
private R8Command(boolean printHelp, boolean printVersion) {
@@ -854,6 +871,7 @@
featureSplitConfiguration = null;
synthesizedClassPrefix = null;
skipDump = false;
+ enableMissingLibraryApiModeling = false;
}
public DexItemFactory getDexItemFactory() {
@@ -960,6 +978,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;
AssertionsConfiguration.Builder builder = AssertionsConfiguration.builder(getReporter());
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 48ac2b9..b44c067 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -256,7 +256,6 @@
enableInitializedClassesAnalysis = false;
callSiteOptimizationOptions.disableOptimization();
horizontalClassMergerOptions.setRestrictToSynthetics();
- apiModelTestingOptions.disableApiCallerIdentification();
}
public boolean printTimes = System.getProperty("com.android.tools.r8.printtimes") != null;
@@ -1669,6 +1668,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 61a9e78..4f31982 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassInstanceInitTest.java
@@ -65,7 +65,7 @@
.applyIf(isGreaterOrEqualToMockLevel(), b -> b.addBootClasspathClasses(LibraryClass.class))
.run(parameters.getRuntime(), Main.class)
.apply(this::checkOutput)
- .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses);
+ .inspect(this::inspect);
}
@Test
@@ -73,8 +73,6 @@
assumeTrue(parameters.isDexRuntime());
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 ec4a09e..c847ac9 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingTest.java
@@ -57,7 +57,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);
@@ -68,8 +68,6 @@
assumeTrue(parameters.isDexRuntime());
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 184e984..5e91b46 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -81,10 +81,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
@@ -92,8 +92,6 @@
assumeTrue(parameters.isDexRuntime());
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 78c5fc1..322b30f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -64,10 +64,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
@@ -75,8 +75,6 @@
assumeTrue(parameters.isDexRuntime());
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 d6911db..5d4fc8f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -74,7 +74,6 @@
.setMode(CompilationMode.DEBUG)
.apply(this::setupTestBuilder)
.compile()
- .inspect(ApiModelingTestHelper::assertNoSynthesizedClasses)
.applyIf(
addLibraryClassesToBootClasspath(),
b -> b.addBootClasspathClasses(LibraryClass.class, LibraryInterface.class))
@@ -82,7 +81,8 @@
addOtherLibraryClassesToBootClasspath(),
b -> b.addBootClasspathClasses(OtherLibraryClass.class))
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkOutput);
+ .apply(this::checkOutput)
+ .inspect(this::inspect);
}
@Test
@@ -90,8 +90,6 @@
assumeTrue(parameters.isDexRuntime());
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 a3183b0..763a7f9 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.assumeTrue;
@@ -79,8 +78,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
@@ -94,7 +92,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) {
@@ -106,16 +104,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 94f733d..f11b7a1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -115,6 +115,7 @@
static void enableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
+ options.apiModelingOptions().enableApiCallerIdentification = true;
options.apiModelingOptions().enableStubbingOfClasses = true;
});
}
@@ -122,6 +123,7 @@
static void enableOutliningOfMethods(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
+ options.apiModelingOptions().enableApiCallerIdentification = true;
options.apiModelingOptions().enableOutliningOfMethods = true;
});
}
@@ -130,6 +132,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 70aa283..0dcd986 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.compilerapi.sourcefile.CustomSourceFileTest;
import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
+import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -41,7 +42,10 @@
DesugarDependenciesTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(GlobalSyntheticsTest.ApiTest.class, CommandLineParserTest.ApiTest.class);
+ ImmutableList.of(
+ GlobalSyntheticsTest.ApiTest.class,
+ CommandLineParserTest.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();
+ }
+ }
+}