Ensure that merged parameter is api safe Bug: b/289361079 Change-Id: Ifa30fed27c283906b9ce89835ba8c268c9430190
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java index 62531ba..625b127 100644 --- a/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java +++ b/src/main/java/com/android/tools/r8/graph/DexTypeUtils.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.utils.AndroidApiLevelUtils; import com.google.common.collect.Iterables; public class DexTypeUtils { @@ -15,7 +16,7 @@ AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) { TypeElement join = TypeElement.join(Iterables.transform(types, type -> type.toTypeElement(appView)), appView); - return toDexType(appView, join); + return findApiSafeUpperBound(appView, toDexType(appView, join)); } public static DexType toDexType( @@ -39,4 +40,23 @@ } return dexItemFactory.objectType; } + + public static DexType findApiSafeUpperBound( + AppView<? extends AppInfoWithClassHierarchy> appView, DexType type) { + DexItemFactory factory = appView.dexItemFactory(); + if (type.toBaseType(factory).isPrimitiveType()) { + return type; + } + DexClass clazz = appView.definitionFor(type.isArrayType() ? type.toBaseType(factory) : type); + if (clazz == null) { + assert false : "We should not have found an upper bound if the hierarchy is missing"; + return type; + } + if (!clazz.isLibraryClass() + || AndroidApiLevelUtils.isApiSafeForReference(clazz.asLibraryClass(), appView)) { + return type; + } + // Always just return the object type since this is safe for all api versions. + return factory.objectType; + } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java index 54bf153..27890cf 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClInitMergeSuperTypeApiLevelTest.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.ToolHelper; import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; +import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.lang.reflect.Constructor; @@ -38,11 +39,18 @@ return getTestParameters().withAllRuntimesAndApiLevels().build(); } + private TypeReference getMergeReferenceForApiLevel() { + boolean canUseExecutable = + parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O); + return Reference.typeFromTypeName(typeName(canUseExecutable ? Executable.class : Object.class)); + } + @Test public void testR8() throws Exception { testForR8(parameters.getBackend()) // Emulate a standard AGP setup where we compile with a new android jar on boot classpath. - .addLibraryFiles(ToolHelper.getMostRecentAndroidJar()) + .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST)) .addInnerClasses(getClass()) .setMinApi(parameters) .addKeepClassAndMembersRules(Main.class) @@ -56,17 +64,11 @@ assertThat(clazz, isPresent()); MethodSubject init = clazz.uniqueInstanceInitializer(); assertThat(init, isPresent()); - TypeReference executableTypeRef = - Reference.typeFromTypeName(typeName(Executable.class)); - // TODO(b/289361079): This should not be of type Executable since this was introduced - // at api 26. - assertEquals(executableTypeRef, init.getParameter(0).getTypeReference()); - // TODO(b/289361079): This should not be of type Executable since this was introduced - // at api 26. + TypeReference mergeTypeRef = getMergeReferenceForApiLevel(); + assertEquals(mergeTypeRef, init.getParameter(0).getTypeReference()); assertTrue( clazz.allFields().stream() - .anyMatch( - f -> executableTypeRef.equals(f.getFinalReference().getFieldType()))); + .anyMatch(f -> mergeTypeRef.equals(f.getFinalReference().getFieldType()))); }) .run(parameters.getRuntime(), Main.class) // The test succeeds for some unknown reason.