blob: bdc9c67e854d9d5f2896020f0d6ddd18476cf160 [file] [log] [blame]
// Copyright (c) 2021, 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.graph;
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 {
public static DexType computeApiSafeLeastUpperBound(
AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
DexType leastUpperBound = computeLeastUpperBound(appView, types);
return findApiSafeUpperBound(appView, leastUpperBound);
}
public static DexType computeLeastUpperBound(
AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
TypeElement join =
TypeElement.join(Iterables.transform(types, type -> type.toTypeElement(appView)), appView);
return toDexType(appView, join);
}
public static boolean isApiSafe(
AppView<? extends AppInfoWithClassHierarchy> appView, DexType type) {
DexType apiSafeUpperBound = findApiSafeUpperBound(appView, type);
return apiSafeUpperBound.isIdenticalTo(type);
}
public static boolean isLeastUpperBoundApiSafe(
AppView<? extends AppInfoWithClassHierarchy> appView, Iterable<DexType> types) {
return isApiSafe(appView, computeLeastUpperBound(appView, types));
}
public static DexType toDexType(
AppView<? extends AppInfoWithClassHierarchy> appView, TypeElement type) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (type.isPrimitiveType()) {
return type.asPrimitiveType().toDexType(dexItemFactory);
}
if (type.isArrayType()) {
ArrayTypeElement arrayType = type.asArrayType();
DexType baseType = toDexType(appView, arrayType.getBaseType());
return baseType.toArrayType(arrayType.getNesting(), dexItemFactory);
}
assert type.isClassType();
ClassTypeElement classType = type.asClassType();
if (classType.getClassType().isNotIdenticalTo(dexItemFactory.objectType)) {
return classType.getClassType();
}
if (classType.getInterfaces().hasSingleKnownInterface()) {
return classType.getInterfaces().getSingleKnownInterface();
}
return dexItemFactory.objectType;
}
public static DexType findApiSafeUpperBound(
AppView<? extends AppInfoWithClassHierarchy> appView, DexType type) {
DexItemFactory factory = appView.dexItemFactory();
DexType baseType = type.toBaseType(factory);
if (baseType.isPrimitiveType()) {
return type;
}
DexClass clazz = appView.definitionFor(baseType);
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)
|| !clazz.hasSuperType()) {
return type;
}
// Return the nearest API safe supertype.
return findApiSafeUpperBound(appView, clazz.getSuperType());
}
public static boolean isTypeAccessibleInMethodContext(
AppView<?> appView, DexType type, ProgramMethod context) {
if (type.isPrimitiveType()) {
return true;
}
if (type.isIdenticalTo(context.getHolderType())
|| (context.getHolder().hasSuperType()
&& type.isIdenticalTo(context.getHolder().getSuperType()))
|| context.getHolder().getInterfaces().contains(type)) {
// In principle we don't know if the supertypes are guaranteed to be accessible in the current
// context. However, if they aren't, the current class will never be successfully loaded
// anyway.
return true;
}
DexClass clazz = appView.definitionFor(type, context);
if (clazz == null) {
return false;
}
if (clazz.isLibraryClass()) {
return AndroidApiLevelUtils.isApiSafeForReference(clazz.asLibraryClass(), appView);
}
if (appView.hasClassHierarchy()) {
return AccessControl.isClassAccessible(clazz, context, appView.withClassHierarchy()).isTrue();
}
return false;
}
}