[ApiModel] Redefine interface for android api database
This is a precursor for changing the internals of the database
Bug: 199934316
Change-Id: I0efa182f63c09fb211fb37533dd3bce6d8310868
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabase.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabase.java
new file mode 100644
index 0000000..5dda285
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabase.java
@@ -0,0 +1,19 @@
+// 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.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public interface AndroidApiLevelDatabase {
+
+ AndroidApiLevel getTypeApiLevel(DexType type);
+
+ AndroidApiLevel getMethodApiLevel(DexMethod method);
+
+ AndroidApiLevel getFieldApiLevel(DexField field);
+}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java
new file mode 100644
index 0000000..d98013e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseImpl.java
@@ -0,0 +1,120 @@
+// 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.apimodel.AndroidApiDatabaseBuilder;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.HashMap;
+import java.util.function.BiFunction;
+
+public class AndroidApiLevelDatabaseImpl implements AndroidApiLevelDatabase {
+
+ private final HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup;
+
+ private final AndroidApiClass SENTINEL =
+ new AndroidApiClass(null) {
+
+ @Override
+ public AndroidApiLevel getApiLevel() {
+ return null;
+ }
+
+ @Override
+ public int getMemberCount() {
+ return 0;
+ }
+
+ @Override
+ protected TraversalContinuation visitFields(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass) {
+ return null;
+ }
+
+ @Override
+ protected TraversalContinuation visitMethods(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor,
+ ClassReference holder,
+ int minApiClass) {
+ return null;
+ }
+ };
+
+ public AndroidApiLevelDatabaseImpl(HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) {
+ this.predefinedApiTypeLookup = predefinedApiTypeLookup;
+ }
+
+ @Override
+ public AndroidApiLevel getTypeApiLevel(DexType type) {
+ return lookupDefinedApiLevel(type);
+ }
+
+ @Override
+ public AndroidApiLevel getMethodApiLevel(DexMethod method) {
+ return lookupDefinedApiLevel(method);
+ }
+
+ @Override
+ public AndroidApiLevel getFieldApiLevel(DexField field) {
+ return lookupDefinedApiLevel(field);
+ }
+
+ private AndroidApiLevel lookupDefinedApiLevel(DexReference reference) {
+ AndroidApiClass foundClass =
+ predefinedApiTypeLookup.getOrDefault(reference.getContextType(), SENTINEL);
+ if (foundClass == null) {
+ return AndroidApiLevel.UNKNOWN;
+ }
+ AndroidApiClass androidApiClass;
+ if (foundClass == SENTINEL) {
+ androidApiClass =
+ AndroidApiDatabaseBuilder.buildClass(reference.getContextType().asClassReference());
+ if (androidApiClass == null) {
+ predefinedApiTypeLookup.put(reference.getContextType(), null);
+ return AndroidApiLevel.UNKNOWN;
+ }
+ } else {
+ androidApiClass = foundClass;
+ }
+ return reference.apply(
+ type -> androidApiClass.getApiLevel(),
+ field -> {
+ FieldReference fieldReference = field.asFieldReference();
+ Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
+ androidApiClass.visitFields(
+ (fieldRef, apiLevel) -> {
+ if (fieldReference.equals(fieldRef)) {
+ apiLevelBox.set(apiLevel);
+ return TraversalContinuation.BREAK;
+ }
+ return TraversalContinuation.CONTINUE;
+ });
+ return apiLevelBox.get();
+ },
+ method -> {
+ MethodReference methodReference = method.asMethodReference();
+ Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
+ androidApiClass.visitMethods(
+ (methodRef, apiLevel) -> {
+ if (methodReference.equals(methodRef)) {
+ apiLevelBox.set(apiLevel);
+ return TraversalContinuation.BREAK;
+ }
+ return TraversalContinuation.CONTINUE;
+ });
+ return apiLevelBox.get();
+ });
+ }
+}
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 e3fa030..8ca1b59 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -4,37 +4,28 @@
package com.android.tools.r8.androidapi;
-import com.android.tools.r8.apimodel.AndroidApiDatabaseBuilder;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMember;
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 com.android.tools.r8.utils.Box;
-import com.android.tools.r8.utils.TraversalContinuation;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
public class AndroidApiReferenceLevelCache {
- private static final int BUILD_CACHE_TRESHOLD = 20;
-
- private final ConcurrentHashMap<DexType, AndroidApiClass> apiTypeLookup;
- private final ConcurrentHashMap<DexReference, AndroidApiLevel> apiMemberLookup =
- new ConcurrentHashMap<>();
private final DesugaredLibraryConfiguration desugaredLibraryConfiguration;
+ private final AndroidApiLevelDatabase androidApiLevelDatabase;
private final AppView<?> appView;
private AndroidApiReferenceLevelCache(AppView<?> appView) {
- this(appView, new ConcurrentHashMap<>());
+ this(appView, new HashMap<>());
}
private AndroidApiReferenceLevelCache(
- AppView<?> appView, ConcurrentHashMap<DexType, AndroidApiClass> apiTypeLookup) {
+ AppView<?> appView, HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup) {
this.appView = appView;
- this.apiTypeLookup = apiTypeLookup;
+ androidApiLevelDatabase = new AndroidApiLevelDatabaseImpl(predefinedApiTypeLookup);
desugaredLibraryConfiguration = appView.options().desugaredLibraryConfiguration;
}
@@ -51,16 +42,16 @@
}
// The apiTypeLookup is build lazily except for the mocked api types that we define in tests
// externally.
- ConcurrentHashMap<DexType, AndroidApiClass> apiTypeLookup = new ConcurrentHashMap<>();
+ HashMap<DexType, AndroidApiClass> predefinedApiTypeLookup = new HashMap<>();
appView
.options()
.apiModelingOptions()
.visitMockedApiReferences(
(classReference, androidApiClass) ->
- apiTypeLookup.put(
+ predefinedApiTypeLookup.put(
appView.dexItemFactory().createType(classReference.getDescriptor()),
androidApiClass));
- return new AndroidApiReferenceLevelCache(appView, apiTypeLookup);
+ return new AndroidApiReferenceLevelCache(appView, predefinedApiTypeLookup);
}
public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
@@ -87,73 +78,9 @@
// of the program.
return appView.options().minApiLevel;
}
- AndroidApiClass androidApiClass =
- apiTypeLookup.computeIfAbsent(
- contextType, type -> AndroidApiDatabaseBuilder.buildClass(type.asClassReference()));
- if (androidApiClass == null) {
- // This is a library class but we have no api model for it. This happens if using an older
- // version of R8 to compile a new target. We simply have to disallow inlining of methods
- // that has such references.
- return AndroidApiLevel.UNKNOWN;
- }
- if (reference.isDexType()) {
- return androidApiClass.getApiLevel();
- }
- return androidApiClass.getMemberCount() > BUILD_CACHE_TRESHOLD
- ? findMemberByCaching(reference, androidApiClass)
- : findMemberByIteration(reference.asDexMember(), androidApiClass);
- }
-
- private AndroidApiLevel findMemberByIteration(
- DexMember<?, ?> reference, AndroidApiClass apiClass) {
- DexItemFactory factory = appView.dexItemFactory();
- // Similar to the case for api classes we are unable to find, if the member
- // is unknown we have to be conservative.
- Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
- reference.apply(
- field ->
- apiClass.visitFields(
- (fieldReference, apiLevel) -> {
- if (factory.createField(fieldReference) == field) {
- apiLevelBox.set(apiLevel);
- return TraversalContinuation.BREAK;
- }
- return TraversalContinuation.CONTINUE;
- }),
- method ->
- apiClass.visitMethods(
- (methodReference, apiLevel) -> {
- if (factory.createMethod(methodReference) == method) {
- apiLevelBox.set(apiLevel);
- return TraversalContinuation.BREAK;
- }
- return TraversalContinuation.CONTINUE;
- }));
- return apiLevelBox.get();
- }
-
- private AndroidApiLevel findMemberByCaching(DexReference reference, AndroidApiClass apiClass) {
- buildCacheForMembers(reference.getContextType(), apiClass);
- return apiMemberLookup.getOrDefault(reference, AndroidApiLevel.UNKNOWN);
- }
-
- private void buildCacheForMembers(DexType context, AndroidApiClass apiClass) {
- assert apiClass.getMemberCount() > BUILD_CACHE_TRESHOLD;
- // Use the context type as a token for us having build a cache for it.
- if (apiMemberLookup.containsKey(context)) {
- return;
- }
- DexItemFactory factory = appView.dexItemFactory();
- apiClass.visitFields(
- (fieldReference, apiLevel) -> {
- apiMemberLookup.put(factory.createField(fieldReference), apiLevel);
- return TraversalContinuation.CONTINUE;
- });
- apiClass.visitMethods(
- (methodReference, apiLevel) -> {
- apiMemberLookup.put(factory.createMethod(methodReference), apiLevel);
- return TraversalContinuation.CONTINUE;
- });
- apiMemberLookup.put(context, AndroidApiLevel.UNKNOWN);
+ return reference.apply(
+ androidApiLevelDatabase::getTypeApiLevel,
+ androidApiLevelDatabase::getFieldApiLevel,
+ androidApiLevelDatabase::getMethodApiLevel);
}
}