Optimize supertype traversal during subtype checks
This splits AppInfoWithClassHierarchy#traverseSuperTypes into two methods traverseSuperTypes and traverseSuperInterfaces, where the latter only gives callbacks for the super interfaces.
Moreover, the traverseSuper* methods are extended to support stopping the upwards class hierarchy traversal from a given class (specified by a predicate).
This is used to optimize subtype checks in the following way:
* If the superclass of interest is an interface, then the new traverseSuperInterfaces is used to skip the initial superclass chain traversal, which is known in advance not to find the superinterface of interest.
* If the superclass of interest is a program class or interface, then the upwards class hierarchy traversal is stopped at the class/library boundary, so that classes and interfaces in the library will not be considered.
Bug: b/422947619
Bug: b/424006396
Change-Id: Id4bf5966b56fd10a2fcb55167bb6f1270ec46bce
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index 5079e73..1efa5bc 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -225,6 +225,8 @@
internal.minimalMainDex = internal.debug;
assert internal.retainCompileTimeAnnotations;
internal.retainCompileTimeAnnotations = false;
+ // Disable fast path in AppInfoWithClassHierarchy#isSubtype.
+ internal.getTestingOptions().allowLibraryExtendsProgramInFullMode = true;
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
index a5f8741..8966b94 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.features.ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap;
+import static com.android.tools.r8.utils.BiPredicateUtils.alwaysFalse;
import static com.android.tools.r8.utils.TraversalContinuation.breakIf;
import static com.android.tools.r8.utils.TraversalContinuation.doBreak;
import static com.android.tools.r8.utils.TraversalContinuation.doContinue;
@@ -20,7 +21,6 @@
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.TriConsumer;
import com.android.tools.r8.utils.TriFunction;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.timing.Timing;
@@ -34,6 +34,7 @@
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.BiPredicate;
import java.util.function.Function;
/* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a
@@ -191,92 +192,170 @@
return doContinue();
}
+ public interface TraverseSuperTypesCallback<TB, TC> {
+
+ TraversalContinuation<TB, TC> apply(
+ DexType supertype, DexClass superclassOrNull, DexClass context, boolean isInterface);
+ }
+
+ public <B> TraversalContinuation<B, ?> traverseSuperInterfaces(
+ DexClass clazz, TraverseSuperTypesCallback<B, ?> fn) {
+ return traverseSuperInterfaces(clazz, fn, alwaysFalse());
+ }
+
/**
- * Primitive traversal over all supertypes of a given type.
+ * Primitive traversal over all superinterfaces of a given type.
*
* <p>No order is guaranteed for the traversal, but a given type will be visited at most once. The
* given type is *not* visited. The function indicates if traversal should continue or break. The
* result of the traversal is BREAK iff the function returned BREAK.
+ *
+ * @param stoppingCriterion if this predicate returns true for a given class, the traversal will
+ * not be applied to the class or any of its supertypes. All supertypes of the given class
+ * that are lower in the hierarchy than classes matched by the stopping criterion will still
+ * be visited.
+ */
+ public <B> TraversalContinuation<B, ?> traverseSuperInterfaces(
+ DexClass clazz,
+ TraverseSuperTypesCallback<B, ?> fn,
+ BiPredicate<DexType, DexClass> stoppingCriterion) {
+ return internalTraverseSuperTypes(clazz, fn, stoppingCriterion, true);
+ }
+
+ public <B> TraversalContinuation<B, ?> traverseSuperTypes(
+ DexClass clazz, TraverseSuperTypesCallback<B, ?> fn) {
+ return traverseSuperTypes(clazz, fn, alwaysFalse());
+ }
+
+ /**
+ * Primitive traversal over all supertypes (including interfaces) of a given type.
+ *
+ * <p>No order is guaranteed for the traversal, but a given type will be visited at most once. The
+ * given type is *not* visited. The function indicates if traversal should continue or break. The
+ * result of the traversal is BREAK iff the function returned BREAK.
+ *
+ * @param stoppingCriterion if this predicate returns true for a given class, the traversal will
+ * not be applied to the class or any of its supertypes. All supertypes of the given class
+ * that are lower in the hierarchy than classes matched by the stopping criterion will still
+ * be visited.
*/
public <B> TraversalContinuation<B, ?> traverseSuperTypes(
- final DexClass clazz,
- TriFunction<DexType, DexClass, Boolean, TraversalContinuation<B, ?>> fn) {
+ DexClass clazz,
+ TraverseSuperTypesCallback<B, ?> fn,
+ BiPredicate<DexType, DexClass> stoppingCriterion) {
+ return internalTraverseSuperTypes(clazz, fn, stoppingCriterion, false);
+ }
+
+ public <B> TraversalContinuation<B, ?> internalTraverseSuperTypes(
+ DexClass clazz,
+ TraverseSuperTypesCallback<B, ?> fn,
+ BiPredicate<DexType, DexClass> stoppingCriterion,
+ boolean interfacesOnly) {
// We do an initial zero-allocation pass over the class super chain as it does not require a
// worklist/seen-set. Only if the traversal is not aborted and there actually are interfaces,
// do we continue traversal over the interface types. This is assuming that the second pass
// over the super chain is less expensive than the eager allocation of the worklist.
- int interfaceCount = 0;
- {
+ DexType stoppingCriterionClassResult = null;
+ if (!interfacesOnly) {
+ boolean seenInterfaces = false;
DexClass currentClass = clazz;
while (currentClass != null) {
- interfaceCount += currentClass.interfaces.values.length;
- if (currentClass.superType == null) {
+ if (!currentClass.getInterfaces().isEmpty()) {
+ seenInterfaces = true;
+ }
+ if (!currentClass.hasSuperType()) {
+ break;
+ }
+ DexType supertype = currentClass.getSuperType();
+ DexClass superclass = definitionFor(supertype);
+ // If the stopping criterion matches the current superclass, then stop the upwards traversal
+ // at this point. Note that at this point we have already forwarded the subclasses to the
+ // consumer, which is WAI.
+ if (stoppingCriterion.test(supertype, superclass)) {
+ stoppingCriterionClassResult = supertype;
break;
}
TraversalContinuation<B, ?> stepResult =
- fn.apply(currentClass.superType, currentClass, false);
+ fn.apply(supertype, superclass, currentClass, false);
if (stepResult.shouldBreak()) {
return stepResult;
}
- currentClass = definitionFor(currentClass.superType);
+ currentClass = definitionFor(currentClass.getSuperType());
+ }
+ if (!seenInterfaces) {
+ return doContinue();
}
}
- if (interfaceCount == 0) {
- return doContinue();
- }
- // Interfaces exist, create a worklist and seen set to ensure single visits.
- Set<DexType> seen = Sets.newIdentityHashSet();
- Deque<DexType> worklist = new ArrayDeque<>();
+ // Interfaces exist (or the superclass traversal has been skipped). Create a worklist with a
+ // seen set to ensure single visits.
+ Set<DexType> seenInterfaces = Sets.newIdentityHashSet();
+ Deque<DexClass> worklist = new ArrayDeque<>();
// Populate the worklist with the direct interfaces of the super chain.
{
DexClass currentClass = clazz;
while (currentClass != null) {
- for (DexType iface : currentClass.interfaces.values) {
- if (seen.add(iface)) {
- TraversalContinuation<B, ?> stepResult = fn.apply(iface, currentClass, true);
- if (stepResult.shouldBreak()) {
- return stepResult;
- }
- worklist.addLast(iface);
- }
+ TraversalContinuation<B, ?> traversalContinuation =
+ internalTraverseInterfaces(
+ currentClass, fn, stoppingCriterion, seenInterfaces, worklist);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
- if (currentClass.superType == null) {
+ if (!currentClass.hasSuperType()) {
break;
}
- currentClass = definitionFor(currentClass.superType);
+ DexType supertype = currentClass.getSuperType();
+ if (interfacesOnly) {
+ // The stoppingCriterionClassResult has not been set above.
+ assert stoppingCriterionClassResult == null;
+ DexClass superclass = definitionFor(supertype);
+ if (stoppingCriterion.test(supertype, superclass)) {
+ break;
+ }
+ currentClass = superclass;
+ } else if (supertype.isIdenticalTo(stoppingCriterionClassResult)) {
+ break;
+ } else {
+ currentClass = definitionFor(supertype);
+ }
}
}
// Iterate all interfaces.
while (!worklist.isEmpty()) {
- DexType type = worklist.removeFirst();
- DexClass definition = definitionFor(type);
- if (definition != null) {
- for (DexType iface : definition.interfaces.values) {
- if (seen.add(iface)) {
- TraversalContinuation<B, ?> stepResult = fn.apply(iface, definition, true);
- if (stepResult.shouldBreak()) {
- return stepResult;
- }
- worklist.addLast(iface);
- }
- }
+ DexClass currentInterface = worklist.removeFirst();
+ TraversalContinuation<B, ?> traversalContinuation =
+ internalTraverseInterfaces(
+ currentInterface, fn, stoppingCriterion, seenInterfaces, worklist);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
}
return doContinue();
}
- /**
- * Iterate each super type of class.
- *
- * <p>Same as traverseSuperTypes, but unconditionally visits all.
- */
- public void forEachSuperType(DexClass clazz, TriConsumer<DexType, DexClass, Boolean> fn) {
- traverseSuperTypes(
- clazz,
- (superType, subclass, isInterface) -> {
- fn.accept(superType, subclass, isInterface);
- return doContinue();
- });
+ private <B> TraversalContinuation<B, ?> internalTraverseInterfaces(
+ DexClass context,
+ TraverseSuperTypesCallback<B, ?> fn,
+ BiPredicate<DexType, DexClass> stoppingCriterion,
+ Set<DexType> seenInterfaces,
+ Deque<DexClass> worklist) {
+ assert !stoppingCriterion.test(context.getType(), context);
+ for (DexType interfaceType : context.getInterfaces()) {
+ if (seenInterfaces.add(interfaceType)) {
+ DexClass interfaceClass = definitionFor(interfaceType);
+ if (stoppingCriterion.test(interfaceType, interfaceClass)) {
+ continue;
+ }
+ TraversalContinuation<B, ?> stepResult =
+ fn.apply(interfaceType, interfaceClass, context, true);
+ if (stepResult.shouldBreak()) {
+ return stepResult;
+ }
+ if (interfaceClass != null) {
+ worklist.addLast(interfaceClass);
+ }
+ }
+ }
+ return doContinue();
}
public boolean isSubtype(DexType subtype, DexType supertype) {
@@ -292,6 +371,15 @@
return isSubtype(subtype, superclass.getType(), null, superclass, immediateSubtypingInfo);
}
+ public boolean isSubtype(DexClass subclass, DexClass superclass) {
+ return isSubtype(subclass.getType(), superclass.getType(), subclass, superclass);
+ }
+
+ private boolean isSubtype(
+ DexType subtype, DexType supertype, DexClass optionalSubclass, DexClass optionalSuperclass) {
+ return isSubtype(subtype, supertype, optionalSubclass, optionalSuperclass, null);
+ }
+
private boolean isSubtype(
DexType subtype,
DexType supertype,
@@ -363,64 +451,49 @@
assert !superclass.getType().isIdenticalTo(objectType);
return false;
}
+ // Fast-path library less-than program checks.
+ boolean isLibraryAboveProgram =
+ options().isFullMode()
+ && !options().getTestingOptions().allowLibraryExtendsProgramInFullMode;
+ if (isLibraryAboveProgram && subclass.isLibraryClass() && superclass.isProgramClass()) {
+ return false;
+ }
// Check all ancestors of the subclass.
// TODO(b/123506120): Report missing types when the predicate is inconclusive.
- return traverseSuperTypes(
- subclass,
- (supertypeOfSubclass, context, isInterface) ->
- breakIf(supertypeOfSubclass.isIdenticalTo(supertype)))
- .shouldBreak();
- }
-
- public boolean isSubtype(DexClass subclass, DexClass superclass) {
- return superclass.isInterface()
- ? isSubtype(subclass.getType(), superclass.getType())
- : isSubtypeOfClass(subclass, superclass);
- }
-
- @SuppressWarnings("ReferenceEquality")
- public boolean isSubtypeOfClass(DexClass subclass, DexClass superclass) {
- assert subclass != null;
- assert superclass != null;
- assert !superclass.isInterface();
- if (subclass.isInterface()) {
- return superclass.getType() == dexItemFactory().objectType;
+ if (superclass.isInterface()) {
+ // Check all super interfaces of the subclass.
+ BiPredicate<DexType, DexClass> stoppingCriterion =
+ isLibraryAboveProgram && superclass.isProgramClass()
+ ? (supertypeOfSubclass, superclassOfSubclass) ->
+ superclassOfSubclass != null && superclassOfSubclass.isLibraryClass()
+ : alwaysFalse();
+ return traverseSuperInterfaces(
+ subclass,
+ (supertypeOfSubclass, superclassOfSubclass, context, isInterface) ->
+ breakIf(supertypeOfSubclass.isIdenticalTo(supertype)),
+ stoppingCriterion)
+ .shouldBreak();
+ } else {
+ // Check the superclass chain of the subclass.
+ TraversalContinuation<Boolean, ?> result =
+ traverseSuperClasses(
+ subclass,
+ (supertypeOfSubclass, superclassOfSubclass, context) -> {
+ if (supertypeOfSubclass.isIdenticalTo(supertype)) {
+ return doBreak(true);
+ }
+ if (superclassOfSubclass == null) {
+ return doBreak(false);
+ }
+ if (isLibraryAboveProgram
+ && superclass.isProgramClass()
+ && superclassOfSubclass.isLibraryClass()) {
+ return doBreak(false);
+ }
+ return doContinue();
+ });
+ return result.isBreak() && result.asBreak().getValue();
}
- return subclass == superclass || isStrictSubtypeOfClass(subclass, superclass);
- }
-
- @SuppressWarnings("ReferenceEquality")
- public boolean isStrictSubtypeOfClass(DexClass subclass, DexClass superclass) {
- assert subclass != null;
- assert superclass != null;
- assert !subclass.isInterface();
- assert !superclass.isInterface();
- if (subclass == superclass) {
- return false;
- }
- // Treat object special: it is always the superclass even for broken hierarchies.
- if (subclass.getType() == dexItemFactory().objectType) {
- return false;
- }
- if (superclass.getType() == dexItemFactory().objectType) {
- return true;
- }
- TraversalContinuation<Boolean, ?> result =
- traverseSuperClasses(
- subclass,
- (currentType, currentClass, immediateSubclass) -> {
- if (currentType == superclass.getType()) {
- return doBreak(true);
- }
- if (currentClass == null) {
- return doBreak(false);
- }
- if (superclass.isProgramClass() && !currentClass.isProgramClass()) {
- return doBreak(false);
- }
- return doContinue();
- });
- return result.isBreak() && result.asBreak().getValue();
}
public boolean inSameHierarchy(DexType type, DexType other) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 4be374a..40c732b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -555,18 +555,17 @@
return isStatic();
}
- @SuppressWarnings("ReferenceEquality")
public boolean isAtLeastAsVisibleAsOtherInSameHierarchy(
- DexEncodedMethod other, AppView<? extends AppInfoWithClassHierarchy> appView) {
- assert getReference().getProto() == other.getReference().getProto();
- assert appView.isSubtype(getHolderType(), other.getHolderType()).isTrue()
- || appView.isSubtype(other.getHolderType(), getHolderType()).isTrue();
+ DexClassAndMethod other, AppView<? extends AppInfoWithClassHierarchy> appView) {
+ assert getReference().getProto().isIdenticalTo(other.getReference().getProto());
+ assert appView.appInfo().isSubtype(getHolderType(), other.getHolder())
+ || appView.appInfo().isSubtype(other.getHolderType(), getHolderType());
AccessFlags<MethodAccessFlags> accessFlags = getAccessFlags();
AccessFlags<?> otherAccessFlags = other.getAccessFlags();
if (accessFlags.getVisibilityOrdinal() < otherAccessFlags.getVisibilityOrdinal()) {
return false;
} else if (accessFlags.isPrivate()) {
- return getHolderType() == other.getHolderType();
+ return getHolderType().isIdenticalTo(other.getHolderType());
} else if (accessFlags.isPublic()) {
return true;
} else {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 13b5b07..f1fe7cf 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -710,7 +710,7 @@
PinnedPredicate pinnedPredicate) {
// Check that the initial resolution holder is accessible from the context.
AppInfoWithClassHierarchy appInfo = appView.appInfo();
- assert appInfo.isSubtype(initialResolutionHolder.type, resolvedHolder.type)
+ assert appInfo.isSubtype(initialResolutionHolder, resolvedHolder)
: initialResolutionHolder.type + " is not a subtype of " + resolvedHolder.type;
if (context != null && isAccessibleFrom(context, appView).isFalse()) {
return LookupResult.createFailedResult();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 7b23ff6..40e2f1c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -441,8 +441,8 @@
.appInfo()
.traverseSuperTypes(
interfaceClass,
- (superType, subclass, isInterface) -> {
- assert superType.isNotIdenticalTo(interfaceClass.getType())
+ (supertype, superclass, context, isInterface) -> {
+ assert supertype.isNotIdenticalTo(interfaceClass.getType())
: "Interface " + interfaceClass.getTypeName() + " inherits from itself";
return TraversalContinuation.doContinue();
});
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
index 9eaeb9d..f29985e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineRetargetConverter.java
@@ -263,11 +263,11 @@
return appInfo
.traverseSuperTypes(
dexClass,
- (supertype, subclass, isSupertypeAnInterface) ->
+ (supertype, superclass, context, isSupertypeAnInterface) ->
TraversalContinuation.breakIf(
- subclass.isInterface()
- && emulateLibraryInterface.containsKey(subclass.getType())
- && subclass.lookupMethod(methodToFind) != null))
+ context.isInterface()
+ && emulateLibraryInterface.containsKey(context.getType())
+ && context.lookupMethod(methodToFind) != null))
.shouldBreak();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
index 19ab71a..849a899 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -398,7 +398,7 @@
.isPossiblyFalse()
|| !newResolutionResult
.getResolvedMethod()
- .isAtLeastAsVisibleAsOtherInSameHierarchy(resolutionResult.getResolvedMethod(), appView)
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(resolutionResult.getResolutionPair(), appView)
// isOverriding expects both arguments to be not private.
|| (!newResolutionResult.getResolvedMethod().isPrivateMethod()
&& !isOverriding(
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index cd8798d..fbaf256 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -164,10 +164,10 @@
.appInfo()
.traverseSuperTypes(
clazz,
- (superType, superClass, isInterface) -> {
- if (isInterface && superClass.isNotProgramClass()) {
+ (supertype, superclass, context, isInterface) -> {
+ if (isInterface && context.isNotProgramClass()) {
Set<ReservedFieldNamingState> reservedNamingState =
- frontierStatesForInterfaces.get(superType);
+ frontierStatesForInterfaces.get(supertype);
if (reservedNamingState != null) {
reservedNamingState.add(reservationState);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index a16b91f..67a1e76 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -96,9 +96,7 @@
if (targetMethod == null) {
return null;
}
- if (!targetMethod
- .getDefinition()
- .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)) {
+ if (!targetMethod.getDefinition().isAtLeastAsVisibleAsOtherInSameHierarchy(method, appView)) {
return null;
}
if (definition.isStatic()
@@ -243,7 +241,7 @@
if (resolvedMethod.getDefinition().isAbstract()
&& resolvedMethod
.getDefinition()
- .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method, appView)
&& (!resolvedMethod.getHolder().isInterface() || holder.getInterfaces().isEmpty())) {
return resolvedMethod;
}
@@ -267,7 +265,7 @@
|| !singleIfaceResult.getResolvedMethod().isAbstract()
|| !singleIfaceResult
.getResolvedMethod()
- .isAtLeastAsVisibleAsOtherInSameHierarchy(method.getDefinition(), appView)) {
+ .isAtLeastAsVisibleAsOtherInSameHierarchy(method, appView)) {
return null;
}
if (representativeInterfaceMethod == null) {
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 f6440dd..f8116f2 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -153,6 +153,7 @@
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.Visibility;
import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramFieldSet;
@@ -2717,13 +2718,13 @@
// in the tree.
KeepReason keepReason = KeepReason.reachableFromLiveType(context.type);
keepClassAndAllMembers(clazz, keepReason);
- appInfo.forEachSuperType(
+ appInfo.traverseSuperTypes(
clazz,
- (superType, subclass, ignored) -> {
- DexProgramClass superClass = asProgramClassOrNull(appInfo().definitionFor(superType));
- if (superClass != null) {
- keepClassAndAllMembers(superClass, keepReason);
+ (supertype, superclass, subclassOfSuperclass, isInterface) -> {
+ if (superclass != null && superclass.isProgramClass()) {
+ keepClassAndAllMembers(superclass.asProgramClass(), keepReason);
}
+ return TraversalContinuation.doContinue();
});
}
if (appView.getDontWarnConfiguration().matches(context)) {
@@ -3014,8 +3015,7 @@
private void markProgramMethodOverridesAsLive(
InstantiatedObject instantiation, DexProgramClass currentClass) {
- assert instantiation.isLambda()
- || appInfo.isSubtype(instantiation.asClass().getType(), currentClass.type);
+ assert instantiation.isLambda() || appInfo.isSubtype(instantiation.asClass(), currentClass);
getReachableVirtualTargets(currentClass)
.forEach(
(resolutionSearchKey, contexts) ->
diff --git a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
index fbe53c8..963e5db 100644
--- a/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
+++ b/src/main/java/com/android/tools/r8/shaking/SingleTargetLookupCache.java
@@ -57,10 +57,10 @@
instance ->
appInfo.traverseSuperTypes(
instance,
- (superType, subclass, ignore) -> {
- if (seen.add(superType)) {
- positiveCache.remove(superType);
- negativeCache.remove(superType);
+ (supertype, superclass, context, ignore) -> {
+ if (seen.add(supertype)) {
+ positiveCache.remove(supertype);
+ negativeCache.remove(supertype);
return TraversalContinuation.doContinue();
} else {
return TraversalContinuation.doBreak();
diff --git a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
index 1f4d01a..a307db7 100644
--- a/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
+++ b/src/main/java/com/android/tools/r8/shaking/rules/KeepAnnotationMatcherPredicates.java
@@ -107,8 +107,8 @@
return appInfo
.traverseSuperTypes(
clazz,
- (superType, unusedSubClass, unusedIsInterface) -> {
- if (matchesClassName(superType, pattern.getClassNamePattern())) {
+ (supertype, unusedSuperclass, unusedSubClass, unusedIsInterface) -> {
+ if (matchesClassName(supertype, pattern.getClassNamePattern())) {
return TraversalContinuation.doBreak();
}
return TraversalContinuation.doContinue();
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 393b7ec..369a7fc 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -882,6 +882,10 @@
return forceProguardCompatibility;
}
+ public boolean isFullMode() {
+ return !isForceProguardCompatibilityEnabled();
+ }
+
public boolean parseSignatureAttribute() {
return true;
}