Maintain the set of direct and indirectly instantiated types.
Change-Id: I63850f40b9ac5c9868f1f35fca7127e41dd856a6
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index e880fc2..ec97730 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -219,6 +219,11 @@
/** Set of types that are actually instantiated. These cannot be abstract. */
private final SetWithReason<DexProgramClass> instantiatedTypes =
new SetWithReason<>(this::registerClass);
+
+ /** Set of all types that are instantiated, directly or indirectly, thus may be abstract. */
+ private final Set<DexProgramClass> directAndIndirectlyInstantiatedTypes =
+ Sets.newIdentityHashSet();
+
/**
* Set of methods that are the immediate target of an invoke. They might not actually be live but
* are required so that invokes can find the method. If a method is only a target but not live,
@@ -477,7 +482,7 @@
if (!instantiatedLambdas.add(clazz, reason)) {
return;
}
-
+ populateInstantiatedTypesCache(clazz);
markTypeAsLive(clazz.type);
}
@@ -1413,6 +1418,9 @@
if (!instantiatedTypes.add(clazz, reason)) {
return;
}
+
+ populateInstantiatedTypesCache(clazz);
+
collectProguardCompatibilityRule(reason);
if (Log.ENABLED) {
Log.verbose(getClass(), "Class `%s` is instantiated, processing...", clazz);
@@ -1428,6 +1436,24 @@
transitionDependentItemsForInstantiatedClass(clazz);
}
+ private void populateInstantiatedTypesCache(DexProgramClass clazz) {
+ if (!directAndIndirectlyInstantiatedTypes.add(clazz)) {
+ return;
+ }
+ if (clazz.superType != null) {
+ DexProgramClass superClass = getProgramClassOrNull(clazz.superType);
+ if (superClass != null) {
+ populateInstantiatedTypesCache(superClass);
+ }
+ }
+ for (DexType iface : clazz.interfaces.values) {
+ DexProgramClass ifaceClass = getProgramClassOrNull(iface);
+ if (ifaceClass != null) {
+ populateInstantiatedTypesCache(ifaceClass);
+ }
+ }
+ }
+
/**
* Marks all methods live that can be reached by calls previously seen.
*
@@ -1621,7 +1647,9 @@
return;
}
if (clazz.isProgramClass()) {
- instantiatedLambdas.add(clazz.asProgramClass(), KeepReason.instantiatedIn(method));
+ if (!instantiatedLambdas.add(clazz.asProgramClass(), KeepReason.instantiatedIn(method))) {
+ populateInstantiatedTypesCache(clazz.asProgramClass());
+ }
}
}
@@ -1662,21 +1690,7 @@
}
private boolean isInstantiatedOrHasInstantiatedSubtype(DexProgramClass clazz) {
- // TODO(zerny): Cache this or instead compute this from subtype and up when instantiated.
- if (instantiatedTypes.contains(clazz) || instantiatedLambdas.contains(clazz)) {
- return true;
- }
- for (DexType subtype : appInfo.subtypes(clazz.type)) {
- DexProgramClass subClass = getProgramClassOrNull(subtype);
- if (subClass == null) {
- assert appView.definitionFor(subtype) != null;
- return true;
- }
- if (instantiatedTypes.contains(subClass) || instantiatedLambdas.contains(subClass)) {
- return true;
- }
- }
- return false;
+ return directAndIndirectlyInstantiatedTypes.contains(clazz);
}
private void markInstanceFieldAsReachable(DexEncodedField encodedField, KeepReason reason) {