blob: 8ca1b5925b99156644cb2f9461f6199ca37eae6f [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.androidapi;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.HashMap;
public class AndroidApiReferenceLevelCache {
private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
private final AndroidApiLevelDatabase androidApiLevelDatabase;
private final AppView<?> appView;
private AndroidApiReferenceLevelCache(AppView<?> appView) {
this(appView, new HashMap<>());
}
private AndroidApiReferenceLevelCache(
AppView<?> appView, HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) {
this.appView = appView;
androidApiLevelDatabase = new AndroidApiLevelDatabaseImpl(predefinedApiTypeLookup);
desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration;
}
public static AndroidApiReferenceLevelCache create(AppView<?> appView) {
if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
// If enableApiCallerIdentification is not enabled then override lookup to always return
// AndroidApiLevel.B.
return new AndroidApiReferenceLevelCache(appView) {
@Override
public AndroidApiLevel lookup(DexReference reference) {
return AndroidApiLevel.B;
}
};
}
// The apiTypeLookup is build lazily except for the mocked api types that we define in tests
// externally.
HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup = new HashMap<>();
appView
.options()
.apiModelingOptions()
.visitMockedApiReferences(
(classReference, androidApiClass) ->
predefinedApiTypeLookup.put(
appView.dexItemFactory().createType(classReference.getDescriptor()),
androidApiClass));
return new AndroidApiReferenceLevelCache(appView, predefinedApiTypeLookup);
}
public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
return lookup(reference).max(minApiLevel);
}
public AndroidApiLevel lookup(DexReference reference) {
DexType contextType = reference.getContextType();
if (contextType.isArrayType()) {
return lookup(contextType.toBaseType(appView.dexItemFactory()));
}
if (contextType.isPrimitiveType() || contextType.isVoidType()) {
return AndroidApiLevel.B;
}
DexClass clazz = appView.definitionFor(contextType);
if (clazz == null) {
return AndroidApiLevel.UNKNOWN;
}
if (!clazz.isLibraryClass()) {
return appView.options().minApiLevel;
}
if (desugaredLibraryConfiguration.isSupported(reference, appView)) {
// If we end up desugaring the reference, the library classes is bridged by j$ which is part
// of the program.
return appView.options().minApiLevel;
}
return reference.apply(
androidApiLevelDatabase::getTypeApiLevel,
androidApiLevelDatabase::getFieldApiLevel,
androidApiLevelDatabase::getMethodApiLevel);
}
}