[ApiModel] No outlining from already outlined classes
Change-Id: If24fcde19918bafe9884a8f05b59ff5f90ae3348
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index 5f65c95..71ce242 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.desugar.apimodel;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.isOutlinedAtSameOrLowerLevel;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
@@ -122,6 +123,10 @@
assert false : "When computed a known api level we should always have a library class";
return appView.computedMinApiLevel();
}
+ // Check if this is already outlined.
+ if (isOutlinedAtSameOrLowerLevel(context.getHolder(), referenceApiLevel)) {
+ return appView.computedMinApiLevel();
+ }
// Check for protected or package private access flags before outlining.
if (firstLibraryClass.isInterface()
|| instruction.isCheckCast()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
index cd94f57..94abbe5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.api;
+import static com.android.tools.r8.utils.AndroidApiLevelUtils.isOutlinedAtSameOrLowerLevel;
+
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
@@ -98,6 +100,10 @@
if (minApiLevel.isGreaterThanOrEqualTo(apiReferenceLevel)) {
continue;
}
+ // Check if this is already outlined.
+ if (isOutlinedAtSameOrLowerLevel(context.getHolder(), apiReferenceLevel)) {
+ continue;
+ }
DexEncodedMethod synthesizedInstanceInitializer =
createSynthesizedInstanceInitializer(
invokeDirect.getInvokedMethod(),
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 3067de7..1c323cb 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevelUtils.java
@@ -296,4 +296,37 @@
}
return interfaces;
}
+
+ /**
+ * A lot of functionality has already been outlined in androidx. The ordinary pattern for manual
+ * outlining is to create a class with the name ApiXXImpl where XX is the api level. This method
+ * will check the context to see if it matches this pattern in androidx and extract the api level
+ * for comparison with the computed api level.
+ */
+ public static boolean isOutlinedAtSameOrLowerLevel(
+ DexProgramClass clazz, ComputedApiLevel apiLevel) {
+ assert apiLevel.isKnownApiLevel();
+ if (!clazz.getType().getDescriptor().startsWith("Landroidx/")) {
+ return false;
+ }
+ String simpleName = clazz.getSimpleName();
+ int apiIndex = simpleName.indexOf("Api");
+ if (apiIndex < 0) {
+ return false;
+ }
+ int endApiIndex = apiIndex += 3;
+ int implIndex = simpleName.indexOf("Impl");
+ if (implIndex < 0 || implIndex < endApiIndex || (implIndex - endApiIndex) != 2) {
+ return false;
+ }
+ String apiLevelAsString = simpleName.substring(endApiIndex, implIndex);
+ if (!StringUtils.onlyContainsDigits(apiLevelAsString)) {
+ return false;
+ }
+ int apiLevelAsInt = Integer.parseInt(apiLevelAsString);
+ if (apiLevelAsInt < 10 || apiLevelAsInt > AndroidApiLevel.LATEST.getLevel()) {
+ return false;
+ }
+ return apiLevel.asKnownApiLevel().getApiLevel().getLevel() <= apiLevelAsInt;
+ }
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
index ff1875d..0d87b10 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.synthesis.SyntheticItemsTestUtils.syntheticApiOutlineClass;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
@@ -145,16 +146,15 @@
if (parameters.isCfRuntime()) {
assertThat(inspector.clazz(classReference), isPresent());
} else {
- assertThat(inspector.clazz(classReference), notIf(isPresent(), isR8));
+ assertThat(
+ inspector.clazz(classReference),
+ notIf(
+ isPresent(),
+ isR8 && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)));
assertThat(
inspector.clazz(syntheticApiOutlineClass(classReference, 0)),
notIf(isPresent(), parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.R)));
- assertThat(
- inspector.clazz(syntheticApiOutlineClass(classReference, 1)),
- notIf(isPresent(), parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)));
- assertThat(
- inspector.clazz(syntheticApiOutlineClass(classReference, 2)),
- notIf(isPresent(), parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.M)));
+ assertThat(inspector.clazz(syntheticApiOutlineClass(classReference, 1)), not(isPresent()));
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
index 39b1db2..9564922 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
@@ -29,7 +29,7 @@
// This is a reproduction of b/216136762.
@RunWith(Parameterized.class)
-public class ApiModelOutlineMethodProtectedTest extends TestBase {
+class ApiModelOutlineMethodProtectedTest extends TestBase {
private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
private static final AndroidApiLevel methodApiLevel = AndroidApiLevel.O_MR1;