[ApiModel] Enable api modeling
This CL enables api-modeling for all references to ensure no inlining of
methods into other methods with a lower api level. The CL also ensure
that merged classes will have the same api level.
RELNOTES: R8 prevents soft-verification errors by modeling all references to android api's introduced later than min-api. Concretely, R8 disables inlining of methods with a higher api-level into methods with a lower level and merging of classes with different levels.
Bug: 138781768
Bug: 187496508
Fixes: 188388130
Fixes: 204063897
Change-Id: I3e0008384d719eaaf09cde0b12702ec9bed658d3
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
index df4cefd..9a4afa3 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -88,10 +88,12 @@
// of the program.
return appView.options().minApiLevel;
}
- return reference.apply(
- androidApiLevelDatabase::getTypeApiLevel,
- androidApiLevelDatabase::getFieldApiLevel,
- androidApiLevelDatabase::getMethodApiLevel);
+ return reference
+ .apply(
+ androidApiLevelDatabase::getTypeApiLevel,
+ androidApiLevelDatabase::getFieldApiLevel,
+ androidApiLevelDatabase::getMethodApiLevel)
+ .max(appView.options().minApiLevel);
}
private boolean isReferenceToJavaLangObject(DexReference reference) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 0b7b139..8007b17 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -127,8 +127,7 @@
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
// Do not inline if the inlinee is greater than the api caller level.
// TODO(b/188498051): We should not force inline lower api method calls.
- if (reason != Reason.FORCE
- && isApiSafeForInlining(method, singleTarget, appView.options()).isPossiblyFalse()) {
+ if (reason != Reason.FORCE && !isApiSafeForInlining(method, singleTarget, appView.options())) {
whyAreYouNotInliningReporter.reportInlineeHigherApiCall();
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 5f05d8d..228c259 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -880,7 +880,7 @@
return null;
}
// Check the api level is allowed to be inlined.
- if (isApiSafeForInlining(method, singleTarget, appView.options()).isPossiblyFalse()) {
+ if (!isApiSafeForInlining(method, singleTarget, appView.options())) {
return null;
}
// Check that the entire constructor chain can be inlined into the current context.
@@ -910,7 +910,7 @@
return null;
}
// Check the api level is allowed to be inlined.
- if (isApiSafeForInlining(method, encodedParent, appView.options()).isPossiblyFalse()) {
+ if (!isApiSafeForInlining(method, encodedParent, appView.options())) {
return null;
}
parent =
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
index 96c4a2b..73ee4de 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -8,18 +8,17 @@
public class AndroidApiLevelUtils {
- public static OptionalBool isApiSafeForInlining(
+ public static boolean isApiSafeForInlining(
ProgramMethod caller, ProgramMethod inlinee, InternalOptions options) {
if (!options.apiModelingOptions().enableApiCallerIdentification) {
- return OptionalBool.TRUE;
+ return true;
}
if (caller.getHolderType() == inlinee.getHolderType()) {
- return OptionalBool.TRUE;
+ return true;
}
- return OptionalBool.of(
- caller
- .getDefinition()
- .getApiLevel()
- .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode()));
+ return caller
+ .getDefinition()
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(inlinee.getDefinition().getApiLevelForCode());
}
}
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 0fdc11c..88c96ef 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1473,7 +1473,7 @@
public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
- public boolean enableApiCallerIdentification = false;
+ public boolean enableApiCallerIdentification = true;
public boolean checkAllApiReferencesAreSet = true;
public boolean useHashingDatabase = true;
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 46ecc50..699bb50 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.classmerging.vertical;
import static com.android.tools.r8.smali.SmaliBuilder.buildCode;
+import static com.android.tools.r8.utils.AndroidApiLevel.K;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -315,10 +316,13 @@
CF_DIR.resolve("FieldCollisionTest$A.class"),
CF_DIR.resolve("FieldCollisionTest$B.class")
};
- Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.FieldCollisionTest",
- "classmerging.FieldCollisionTest$B");
+ ImmutableSet.Builder<String> preservedNamesBuilder = ImmutableSet.builder();
+ preservedNamesBuilder.add("classmerging.FieldCollisionTest");
+ preservedNamesBuilder.add("classmerging.FieldCollisionTest$B");
+ if (parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(K)) {
+ preservedNamesBuilder.add("classmerging.FieldCollisionTest$A");
+ }
+ Set<String> preservedClassNames = preservedNamesBuilder.build();
runTest(
testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
index 00575cd..bc389c9 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
@@ -12,10 +12,12 @@
import static junit.framework.TestCase.assertTrue;
import com.android.tools.r8.Jdk9TestUtils;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.utils.StringUtils;
@@ -51,33 +53,72 @@
@Test
public void testClassMergingNestMemberRemoval() throws Exception {
- testNestAttributesCorrect(MERGING_OUTER_CLASS, MERGING_OUTER_CLASS, MERGING_EXPECTED_RESULT, 3);
+ testNestAttributesCorrect(
+ MERGING_OUTER_CLASS,
+ MERGING_OUTER_CLASS,
+ MERGING_EXPECTED_RESULT,
+ 3,
+ ThrowableConsumer.empty());
}
@Test
public void testClassMergingNestHostRemoval() throws Exception {
testNestAttributesCorrect(
- MERGING_OUTER_CLASS + "$MiddleOuter", MERGING_OUTER_CLASS, MERGING_EXPECTED_RESULT, 2);
+ MERGING_OUTER_CLASS + "$MiddleOuter",
+ MERGING_OUTER_CLASS,
+ MERGING_EXPECTED_RESULT,
+ 2,
+ builder -> {
+ builder.addOptionsModification(
+ internalOptions -> {
+ // The test makes an invoke to StringConcatFactory which is not known to DEX and
+ // we therefore fail to merge the classes.
+ internalOptions.apiModelingOptions().enableApiCallerIdentification = false;
+ });
+ });
}
@Test
public void testTreePruningNestMemberRemoval() throws Exception {
- testNestAttributesCorrect(PRUNING_OUTER_CLASS, PRUNING_OUTER_CLASS, PRUNING_EXPECTED_RESULT, 2);
+ testNestAttributesCorrect(
+ PRUNING_OUTER_CLASS,
+ PRUNING_OUTER_CLASS,
+ PRUNING_EXPECTED_RESULT,
+ 2,
+ ThrowableConsumer.empty());
}
@Test
public void testTreePruningNestHostRemoval() throws Exception {
testNestAttributesCorrect(
- PRUNING_OUTER_CLASS + "$Pruned", PRUNING_OUTER_CLASS, PRUNING_EXPECTED_RESULT, 1);
+ PRUNING_OUTER_CLASS + "$Pruned",
+ PRUNING_OUTER_CLASS,
+ PRUNING_EXPECTED_RESULT,
+ 1,
+ ThrowableConsumer.empty());
}
public void testNestAttributesCorrect(
- String mainClassName, String outerNestName, String expectedResult, int expectedNumClassesLeft)
+ String mainClassName,
+ String outerNestName,
+ String expectedResult,
+ int expectedNumClassesLeft,
+ ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
throws Exception {
testNestAttributesCorrect(
- mainClassName, outerNestName, expectedResult, true, expectedNumClassesLeft);
+ mainClassName,
+ outerNestName,
+ expectedResult,
+ true,
+ expectedNumClassesLeft,
+ testBuilderConsumer);
testNestAttributesCorrect(
- mainClassName, outerNestName, expectedResult, false, expectedNumClassesLeft);
+ mainClassName,
+ outerNestName,
+ expectedResult,
+ false,
+ expectedNumClassesLeft,
+ testBuilderConsumer);
}
public void testNestAttributesCorrect(
@@ -85,7 +126,8 @@
String outerNestName,
String expectedResult,
boolean minification,
- int expectedNumClassesLeft)
+ int expectedNumClassesLeft,
+ ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
throws Exception {
String actualMainClassName = PACKAGE_NAME + mainClassName;
testForR8(parameters.getBackend())
@@ -101,6 +143,7 @@
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
.addKeepPackageNamesRule("nesthostexample")
.addInliningAnnotations()
+ .apply(testBuilderConsumer)
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
index 693b9bf..26bbf63 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -56,9 +57,9 @@
private void verifyClassInliningRemovesCandidate(CodeInspector inspector) {
ClassSubject candidateClassSubject = inspector.clazz(Candidate.class);
- if (enableClassInlining) {
- // TODO(b/188388130): String.empty was added in AndroidApiLevel.G, so if this fails after
- // enabling api modeling then check for api level.
+ if (enableClassInlining
+ && (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.G))) {
assertThat(candidateClassSubject, not(isPresent()));
} else {
assertThat(candidateClassSubject, isPresent());
diff --git a/tools/chrome_data.py b/tools/chrome_data.py
index 0d4329d..ebae542 100644
--- a/tools/chrome_data.py
+++ b/tools/chrome_data.py
@@ -248,6 +248,7 @@
'inputs': [os.path.join(V180917_BASE, path) for path in INPUT_JARS],
'pgconf': [os.path.join(V180917_BASE, path) for path in PG_CONFS],
'libraries': [os.path.join(V180917_BASE, path) for path in LIBRARIES],
+ 'min-api': '21',
},
},
'200430': {