|  | // Copyright (c) 2020, 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 static com.android.tools.r8.utils.TraversalContinuation.doBreak; | 
|  | import static com.android.tools.r8.utils.TraversalContinuation.doContinue; | 
|  |  | 
|  | import com.android.tools.r8.features.ClassToFeatureSplitMap; | 
|  | import com.android.tools.r8.ir.analysis.type.InterfaceCollection; | 
|  | import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder; | 
|  | import com.android.tools.r8.ir.desugar.LambdaDescriptor; | 
|  | import com.android.tools.r8.shaking.MainDexInfo; | 
|  | import com.android.tools.r8.shaking.MissingClasses; | 
|  | import com.android.tools.r8.synthesis.CommittedItems; | 
|  | import com.android.tools.r8.synthesis.SyntheticItems; | 
|  | import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy; | 
|  | import com.android.tools.r8.utils.Pair; | 
|  | import com.android.tools.r8.utils.Timing; | 
|  | 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.google.common.collect.Sets; | 
|  | import java.util.ArrayDeque; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collections; | 
|  | import java.util.Deque; | 
|  | import java.util.List; | 
|  | import java.util.Map.Entry; | 
|  | import java.util.Set; | 
|  | import java.util.concurrent.ExecutionException; | 
|  | import java.util.concurrent.ExecutorService; | 
|  | import java.util.function.Function; | 
|  |  | 
|  | /* Specific subclass of AppInfo designed to support desugaring in D8. Desugaring requires a | 
|  | * minimal amount of knowledge in the overall program, provided through classpath. Basic | 
|  | * features are present, such as static and super look-ups, or isSubtype. | 
|  | */ | 
|  | public class AppInfoWithClassHierarchy extends AppInfo { | 
|  |  | 
|  | private static final CreateDesugaringViewOnAppInfo WITNESS = new CreateDesugaringViewOnAppInfo(); | 
|  |  | 
|  | static class CreateDesugaringViewOnAppInfo { | 
|  | private CreateDesugaringViewOnAppInfo() {} | 
|  | } | 
|  |  | 
|  | public static AppInfoWithClassHierarchy createInitialAppInfoWithClassHierarchy( | 
|  | DexApplication application, | 
|  | ClassToFeatureSplitMap classToFeatureSplitMap, | 
|  | MainDexInfo mainDexInfo, | 
|  | GlobalSyntheticsStrategy globalSyntheticsStrategy) { | 
|  | return new AppInfoWithClassHierarchy( | 
|  | SyntheticItems.createInitialSyntheticItems(application, globalSyntheticsStrategy), | 
|  | classToFeatureSplitMap, | 
|  | mainDexInfo, | 
|  | MissingClasses.empty()); | 
|  | } | 
|  |  | 
|  | private final ClassToFeatureSplitMap classToFeatureSplitMap; | 
|  |  | 
|  | /** Set of types that are mentioned in the program, but for which no definition exists. */ | 
|  | // TODO(b/175659048): Consider hoisting to AppInfo to allow using MissingClasses in D8 desugar. | 
|  | private final MissingClasses missingClasses; | 
|  |  | 
|  | // For AppInfoWithLiveness subclass. | 
|  | protected AppInfoWithClassHierarchy( | 
|  | CommittedItems committedItems, | 
|  | ClassToFeatureSplitMap classToFeatureSplitMap, | 
|  | MainDexInfo mainDexInfo, | 
|  | MissingClasses missingClasses) { | 
|  | super(committedItems, mainDexInfo); | 
|  | this.classToFeatureSplitMap = classToFeatureSplitMap; | 
|  | this.missingClasses = missingClasses; | 
|  | } | 
|  |  | 
|  | // For desugaring. | 
|  | private AppInfoWithClassHierarchy(CreateDesugaringViewOnAppInfo witness, AppInfo appInfo) { | 
|  | super(witness, appInfo); | 
|  | this.classToFeatureSplitMap = ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(); | 
|  | // TODO(b/175659048): Migrate the reporting of missing classes in D8 desugar to MissingClasses, | 
|  | //  and use the missing classes from AppInfo instead of MissingClasses.empty(). | 
|  | this.missingClasses = MissingClasses.empty(); | 
|  | } | 
|  |  | 
|  | public static AppInfoWithClassHierarchy createForDesugaring(AppInfo appInfo) { | 
|  | assert !appInfo.hasClassHierarchy(); | 
|  | return new AppInfoWithClassHierarchy(WITNESS, appInfo); | 
|  | } | 
|  |  | 
|  | public final AppInfoWithClassHierarchy rebuildWithClassHierarchy(CommittedItems commit) { | 
|  | return new AppInfoWithClassHierarchy( | 
|  | commit, getClassToFeatureSplitMap(), getMainDexInfo(), getMissingClasses()); | 
|  | } | 
|  |  | 
|  | public void notifyMinifierFinished() { | 
|  | // Intentionally empty. | 
|  | } | 
|  |  | 
|  | public AppInfoWithClassHierarchy rebuildWithClassHierarchy( | 
|  | Function<DexApplication, DexApplication> fn) { | 
|  | assert checkIfObsolete(); | 
|  | return new AppInfoWithClassHierarchy( | 
|  | getSyntheticItems().commit(fn.apply(app())), | 
|  | getClassToFeatureSplitMap(), | 
|  | getMainDexInfo(), | 
|  | getMissingClasses()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AppInfoWithClassHierarchy rebuildWithMainDexInfo(MainDexInfo mainDexInfo) { | 
|  | assert getClass() == AppInfoWithClassHierarchy.class; | 
|  | assert checkIfObsolete(); | 
|  | return new AppInfoWithClassHierarchy( | 
|  | getSyntheticItems().commit(app()), | 
|  | getClassToFeatureSplitMap(), | 
|  | mainDexInfo, | 
|  | getMissingClasses()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AppInfoWithClassHierarchy prunedCopyFrom( | 
|  | PrunedItems prunedItems, ExecutorService executorService, Timing timing) | 
|  | throws ExecutionException { | 
|  | assert getClass() == AppInfoWithClassHierarchy.class; | 
|  | assert checkIfObsolete(); | 
|  | assert prunedItems.getPrunedApp() == app(); | 
|  | if (prunedItems.isEmpty()) { | 
|  | return this; | 
|  | } | 
|  | timing.begin("Pruning AppInfoWithClassHierarchy"); | 
|  | AppInfoWithClassHierarchy result = | 
|  | new AppInfoWithClassHierarchy( | 
|  | getSyntheticItems().commitPrunedItems(prunedItems), | 
|  | getClassToFeatureSplitMap().withoutPrunedItems(prunedItems), | 
|  | getMainDexInfo().withoutPrunedItems(prunedItems), | 
|  | getMissingClasses()); | 
|  | timing.end(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | public ClassToFeatureSplitMap getClassToFeatureSplitMap() { | 
|  | return classToFeatureSplitMap; | 
|  | } | 
|  |  | 
|  | public MissingClasses getMissingClasses() { | 
|  | return missingClasses; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasClassHierarchy() { | 
|  | assert checkIfObsolete(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AppInfoWithClassHierarchy withClassHierarchy() { | 
|  | assert checkIfObsolete(); | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** Primitive traversal over all (non-interface) superclasses of a given type. */ | 
|  | public <B> TraversalContinuation<B, ?> traverseSuperClasses( | 
|  | DexClass clazz, TriFunction<DexType, DexClass, DexClass, TraversalContinuation<B, ?>> fn) { | 
|  | DexClass currentClass = clazz; | 
|  | while (currentClass != null && currentClass.getSuperType() != null) { | 
|  | DexClass superclass = definitionFor(currentClass.getSuperType()); | 
|  | TraversalContinuation<B, ?> stepResult = | 
|  | fn.apply(currentClass.getSuperType(), superclass, currentClass); | 
|  | if (stepResult.shouldBreak()) { | 
|  | return stepResult; | 
|  | } | 
|  | currentClass = superclass; | 
|  | } | 
|  | return doContinue(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Primitive traversal over all supertypes 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. | 
|  | */ | 
|  | public <B> TraversalContinuation<B, ?> traverseSuperTypes( | 
|  | final DexClass clazz, | 
|  | TriFunction<DexType, DexClass, Boolean, TraversalContinuation<B, ?>> fn) { | 
|  | // 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; | 
|  | { | 
|  | DexClass currentClass = clazz; | 
|  | while (currentClass != null) { | 
|  | interfaceCount += currentClass.interfaces.values.length; | 
|  | if (currentClass.superType == null) { | 
|  | break; | 
|  | } | 
|  | TraversalContinuation<B, ?> stepResult = | 
|  | fn.apply(currentClass.superType, currentClass, false); | 
|  | if (stepResult.shouldBreak()) { | 
|  | return stepResult; | 
|  | } | 
|  | currentClass = definitionFor(currentClass.superType); | 
|  | } | 
|  | } | 
|  | 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<>(); | 
|  | // 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); | 
|  | } | 
|  | } | 
|  | if (currentClass.superType == null) { | 
|  | break; | 
|  | } | 
|  | currentClass = definitionFor(currentClass.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); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | 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(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public boolean isSubtype(DexType subtype, DexType supertype) { | 
|  | assert subtype != null; | 
|  | assert supertype != null; | 
|  | assert subtype.isClassType(); | 
|  | assert supertype.isClassType(); | 
|  | return subtype == supertype || isStrictSubtypeOf(subtype, supertype); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | public boolean isStrictSubtypeOf(DexType subtype, DexType supertype) { | 
|  | assert subtype != null; | 
|  | assert supertype != null; | 
|  | assert subtype.isClassType(); | 
|  | assert supertype.isClassType(); | 
|  | if (subtype == supertype) { | 
|  | return false; | 
|  | } | 
|  | // Treat object special: it is always the supertype even for broken hierarchies. | 
|  | if (subtype == dexItemFactory().objectType) { | 
|  | return false; | 
|  | } | 
|  | if (supertype == dexItemFactory().objectType) { | 
|  | return true; | 
|  | } | 
|  | if (!subtype.isClassType() || !supertype.isClassType()) { | 
|  | return false; | 
|  | } | 
|  | DexClass clazz = definitionFor(subtype); | 
|  | if (clazz == null) { | 
|  | return false; | 
|  | } | 
|  | // TODO(b/123506120): Report missing types when the predicate is inconclusive. | 
|  | return traverseSuperTypes( | 
|  | clazz, | 
|  | (superType, subclass, isInterface) -> superType == supertype ? doBreak() : doContinue()) | 
|  | .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; | 
|  | } | 
|  | 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) { | 
|  | assert type.isClassType(); | 
|  | assert other.isClassType(); | 
|  | return isSubtype(type, other) || isSubtype(other, type); | 
|  | } | 
|  |  | 
|  | public boolean inDifferentHierarchy(DexType type1, DexType type2) { | 
|  | return !inSameHierarchy(type1, type2); | 
|  | } | 
|  |  | 
|  | public boolean isMissingOrHasMissingSuperType(DexType type) { | 
|  | DexClass clazz = definitionFor(type); | 
|  | return clazz == null || clazz.hasMissingSuperType(this); | 
|  | } | 
|  |  | 
|  | /** Collect all interfaces that this type directly or indirectly implements. */ | 
|  | @SuppressWarnings({"BadImport", "ReferenceEquality"}) | 
|  | public InterfaceCollection implementedInterfaces(DexType type) { | 
|  | assert type.isClassType(); | 
|  | DexClass clazz = definitionFor(type); | 
|  | if (clazz == null) { | 
|  | return InterfaceCollection.empty(); | 
|  | } | 
|  |  | 
|  | // Fast path for a type below object with no interfaces. | 
|  | if (clazz.superType == dexItemFactory().objectType && clazz.interfaces.isEmpty()) { | 
|  | return clazz.isInterface() | 
|  | ? InterfaceCollection.singleton(type) | 
|  | : InterfaceCollection.empty(); | 
|  | } | 
|  |  | 
|  | // Slow path traverses the full super type hierarchy. | 
|  | Builder builder = InterfaceCollection.builder(); | 
|  | if (clazz.isInterface()) { | 
|  | builder.addInterface(type, true); | 
|  | } | 
|  | // First find all interface leafs from the class super-type chain. | 
|  | Set<DexType> seenAndKnown = Sets.newIdentityHashSet(); | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | Deque<Pair<DexClass, Boolean>> worklist = new ArrayDeque<>(); | 
|  | { | 
|  | DexClass implementor = clazz; | 
|  | while (implementor != null) { | 
|  | for (DexType iface : implementor.interfaces) { | 
|  | if (seenAndKnown.contains(iface)) { | 
|  | continue; | 
|  | } | 
|  | boolean isKnown = | 
|  | InterfaceCollection.isKnownToImplement(iface, implementor.getType(), options()); | 
|  | builder.addInterface(iface, isKnown); | 
|  | if (isKnown) { | 
|  | seenAndKnown.add(iface); | 
|  | } | 
|  | DexClass definition = definitionFor(iface); | 
|  | if (definition != null && !definition.interfaces.isEmpty()) { | 
|  | worklist.add(new Pair<>(definition, isKnown)); | 
|  | } | 
|  | } | 
|  | if (implementor.superType == null | 
|  | || implementor.superType == options().dexItemFactory().objectType) { | 
|  | break; | 
|  | } | 
|  | implementor = definitionFor(implementor.superType); | 
|  | } | 
|  | } | 
|  | // Second complete the worklist of interfaces. All paths must be visited as an interface may | 
|  | // be unknown on one but not on another. | 
|  | while (!worklist.isEmpty()) { | 
|  | Pair<DexClass, Boolean> item = worklist.poll(); | 
|  | DexClass implementor = item.getFirst(); | 
|  | assert !implementor.interfaces.isEmpty(); | 
|  | for (DexType itf : implementor.interfaces) { | 
|  | if (seenAndKnown.contains(itf)) { | 
|  | continue; | 
|  | } | 
|  | // A derived interface is known only if the full chain leading to it is known. | 
|  | boolean isKnown = | 
|  | item.getSecond() | 
|  | && InterfaceCollection.isKnownToImplement(itf, implementor.getType(), options()); | 
|  | builder.addInterface(itf, isKnown); | 
|  | if (isKnown) { | 
|  | seenAndKnown.add(itf); | 
|  | } | 
|  | DexClass definition = definitionFor(itf); | 
|  | if (definition != null && !definition.interfaces.isEmpty()) { | 
|  | worklist.add(new Pair<>(definition, isKnown)); | 
|  | } | 
|  | } | 
|  | } | 
|  | return builder.build(); | 
|  | } | 
|  |  | 
|  | public boolean isExternalizable(DexType type) { | 
|  | return isSubtype(type, dexItemFactory().externalizableType); | 
|  | } | 
|  |  | 
|  | public boolean isSerializable(DexType type) { | 
|  | return isSubtype(type, dexItemFactory().serializableType); | 
|  | } | 
|  |  | 
|  | public List<DexProgramClass> computeProgramClassRelationChain( | 
|  | DexProgramClass subClass, DexProgramClass superClass) { | 
|  | assert isSubtype(subClass.type, superClass.type); | 
|  | assert !subClass.isInterface(); | 
|  | if (!superClass.isInterface()) { | 
|  | return computeChainInClassHierarchy(subClass, superClass.type); | 
|  | } | 
|  | // If the super type is an interface we first compute the program chain upwards, and in a | 
|  | // top-down order check if the interface is a super-type to the class. Computing it this way | 
|  | // guarantees to find the instantiated program-classes of the longest chain. | 
|  | List<DexProgramClass> relationChain = | 
|  | computeChainInClassHierarchy(subClass, dexItemFactory().objectType); | 
|  | WorkList<DexType> interfaceWorklist = WorkList.newIdentityWorkList(); | 
|  | for (int i = relationChain.size() - 1; i >= 0; i--) { | 
|  | DexProgramClass clazz = relationChain.get(i); | 
|  | if (isInterfaceInSuperTypes(clazz, superClass.type, interfaceWorklist)) { | 
|  | return relationChain.subList(0, i + 1); | 
|  | } | 
|  | } | 
|  | return Collections.emptyList(); | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | private boolean isInterfaceInSuperTypes( | 
|  | DexProgramClass clazz, DexType ifaceToFind, WorkList<DexType> workList) { | 
|  | workList.addIfNotSeen(clazz.allImmediateSupertypes()); | 
|  | while (workList.hasNext()) { | 
|  | DexType superType = workList.next(); | 
|  | if (superType == ifaceToFind) { | 
|  | return true; | 
|  | } | 
|  | DexClass superClass = definitionFor(superType); | 
|  | if (superClass != null) { | 
|  | workList.addIfNotSeen(superClass.allImmediateSupertypes()); | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | private List<DexProgramClass> computeChainInClassHierarchy( | 
|  | DexProgramClass subClass, DexType superType) { | 
|  | assert isSubtype(subClass.type, superType); | 
|  | assert !subClass.isInterface(); | 
|  | assert superType == dexItemFactory().objectType | 
|  | || definitionFor(superType) == null | 
|  | || !definitionFor(superType).isInterface(); | 
|  | List<DexProgramClass> relationChain = new ArrayList<>(); | 
|  | DexClass current = subClass; | 
|  | while (current != null) { | 
|  | if (current.isProgramClass()) { | 
|  | relationChain.add(current.asProgramClass()); | 
|  | } | 
|  | if (current.type == superType) { | 
|  | return relationChain; | 
|  | } | 
|  | current = definitionFor(current.superType); | 
|  | } | 
|  | return relationChain; | 
|  | } | 
|  |  | 
|  | public boolean methodDefinedInInterfaces(DexEncodedMethod method, DexType implementingClass) { | 
|  | DexClass holder = definitionFor(implementingClass); | 
|  | if (holder == null) { | 
|  | return false; | 
|  | } | 
|  | for (DexType iface : holder.interfaces.values) { | 
|  | if (methodDefinedInInterface(method, iface)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean methodDefinedInInterface(DexEncodedMethod method, DexType iface) { | 
|  | DexClass potentialHolder = definitionFor(iface); | 
|  | if (potentialHolder == null) { | 
|  | return false; | 
|  | } | 
|  | assert potentialHolder.isInterface(); | 
|  | for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods()) { | 
|  | if (virtualMethod.getReference().match(method.getReference()) | 
|  | && virtualMethod.isSameVisibility(method)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | for (DexType parentInterface : potentialHolder.interfaces.values) { | 
|  | if (methodDefinedInInterface(method, parentInterface)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Helper method used for emulated interface resolution (not in JVM specifications). The result | 
|  | * may be abstract. | 
|  | */ | 
|  | public DexClassAndMethod lookupMaximallySpecificMethod(DexClass clazz, DexMethod method) { | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .lookupMaximallySpecificTarget(clazz, method); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Helper methods used for emulated interface resolution (not in JVM specifications). Answers the | 
|  | * abstract interface methods that the resolution could but does not necessarily resolve into. | 
|  | */ | 
|  | public List<Entry<DexClass, DexEncodedMethod>> getAbstractInterfaceMethods( | 
|  | DexClass clazz, DexMethod method) { | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .getAbstractInterfaceMethods(clazz, method); | 
|  | } | 
|  |  | 
|  | MethodResolutionResult resolveMaximallySpecificTarget(DexClass clazz, DexMethod method) { | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMaximallySpecificTarget(clazz, method); | 
|  | } | 
|  |  | 
|  | MethodResolutionResult resolveMaximallySpecificTarget(LambdaDescriptor lambda, DexMethod method) { | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMaximallySpecificTarget(lambda, method); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup instance field starting in type and following the interface and super chain. | 
|  | * | 
|  | * <p>The result is the field that will be hit at runtime, if such field is known. A result of | 
|  | * null indicates that the field is either undefined or not an instance field. | 
|  | */ | 
|  | public DexEncodedField lookupInstanceTargetOn(DexType type, DexField field) { | 
|  | assert checkIfObsolete(); | 
|  | assert type.isClassType(); | 
|  | DexEncodedField result = resolveFieldOn(type, field).getResolvedField(); | 
|  | return result == null || result.accessFlags.isStatic() ? null : result; | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupInstanceTarget(DexField field) { | 
|  | return lookupInstanceTargetOn(field.holder, field); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup static field starting in type and following the interface and super chain. | 
|  | * | 
|  | * <p>The result is the field that will be hit at runtime, if such field is known. A result of | 
|  | * null indicates that the field is either undefined or not a static field. | 
|  | */ | 
|  | public DexClassAndField lookupStaticTargetOn(DexType type, DexField field) { | 
|  | assert checkIfObsolete(); | 
|  | assert type.isClassType(); | 
|  | DexClassAndField result = resolveFieldOn(type, field).getResolutionPair(); | 
|  | return result == null || !result.getAccessFlags().isStatic() ? null : result; | 
|  | } | 
|  |  | 
|  | public DexClassAndField lookupStaticTarget(DexField field) { | 
|  | return lookupStaticTargetOn(field.getHolderType(), field); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup static method following the super chain from the holder of {@code method}. | 
|  | * | 
|  | * <p>This method will resolve the method on the holder of {@code method} and only return a | 
|  | * non-null value if the result of resolution was a static, non-abstract method. | 
|  | * | 
|  | * @param method the method to lookup | 
|  | * @return The actual target for {@code method} or {@code null} if none found. | 
|  | */ | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public DexClassAndMethod lookupStaticTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupStaticTarget(method, context, appView, appView.appInfo()); | 
|  | } | 
|  |  | 
|  | public DexClassAndMethod lookupStaticTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | assert checkIfObsolete(); | 
|  | return unsafeResolveMethodDueToDexFormatLegacy(method) | 
|  | .lookupInvokeStaticTarget(context, appView, appInfo); | 
|  | } | 
|  |  | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public DexClassAndMethod lookupStaticTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupStaticTarget(method, context.getHolder(), appView); | 
|  | } | 
|  |  | 
|  | public DexClassAndMethod lookupStaticTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | return lookupStaticTarget(method, context.getHolder(), appView, appInfo); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup super method following the super chain from the holder of {@code method}. | 
|  | * | 
|  | * <p>This method will resolve the method on the holder of {@code method} and only return a | 
|  | * non-null value if the result of resolution was an instance (i.e. non-static) method. | 
|  | * | 
|  | * @param method the method to lookup | 
|  | * @param context the class the invoke is contained in, i.e., the holder of the caller. | 
|  | * @return The actual target for {@code method} or {@code null} if none found. | 
|  | */ | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public DexClassAndMethod lookupSuperTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupSuperTarget(method, context, appView, appView.appInfo()); | 
|  | } | 
|  |  | 
|  | public DexClassAndMethod lookupSuperTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | assert checkIfObsolete(); | 
|  | return unsafeResolveMethodDueToDexFormatLegacy(method) | 
|  | .lookupInvokeSuperTarget(context, appView, appInfo); | 
|  | } | 
|  |  | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public final DexClassAndMethod lookupSuperTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupSuperTarget(method, context, appView, appView.appInfo()); | 
|  | } | 
|  |  | 
|  | public final DexClassAndMethod lookupSuperTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | return lookupSuperTarget(method, context.getHolder(), appView, appInfo); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup direct method following the super chain from the holder of {@code method}. | 
|  | * | 
|  | * <p>This method will lookup private and constructor methods. | 
|  | * | 
|  | * @param method the method to lookup | 
|  | * @return The actual target for {@code method} or {@code null} if none found. | 
|  | */ | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public DexClassAndMethod lookupDirectTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupDirectTarget(method, context, appView, appView.appInfo()); | 
|  | } | 
|  |  | 
|  | public DexClassAndMethod lookupDirectTarget( | 
|  | DexMethod method, | 
|  | DexProgramClass context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | assert checkIfObsolete(); | 
|  | return unsafeResolveMethodDueToDexFormatLegacy(method) | 
|  | .lookupInvokeDirectTarget(context, appView, appInfo); | 
|  | } | 
|  |  | 
|  | // TODO(b/155968472): This should take a parameter `boolean isInterface` and use resolveMethod(). | 
|  | public DexClassAndMethod lookupDirectTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return lookupDirectTarget(method, context, appView, appView.appInfo()); | 
|  | } | 
|  |  | 
|  | public DexClassAndMethod lookupDirectTarget( | 
|  | DexMethod method, | 
|  | ProgramMethod context, | 
|  | AppView<?> appView, | 
|  | AppInfoWithClassHierarchy appInfo) { | 
|  | return lookupDirectTarget(method, context.getHolder(), appView, appInfo); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * This method will query the definition of the holder to decide on which resolution to use. | 
|  | * | 
|  | * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the | 
|  | * kind of a method reference. | 
|  | */ | 
|  | public MethodResolutionResult unsafeResolveMethodDueToDexFormatLegacy(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .unsafeResolveMethodDueToDexFormat(method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodLegacy(DexMethod invokedMethod, boolean isInterface) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnLegacy(invokedMethod.getHolderType(), invokedMethod, isInterface); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOn(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return clazz.isInterface() | 
|  | ? resolveMethodOnInterface(clazz, method) | 
|  | : resolveMethodOnClass(clazz, method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnLegacy(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return clazz.isInterface() | 
|  | ? resolveMethodOnInterfaceLegacy(clazz, method) | 
|  | : resolveMethodOnClassLegacy(clazz, method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnLegacy( | 
|  | DexClass clazz, DexMethodSignature methodSignature) { | 
|  | assert checkIfObsolete(); | 
|  | return clazz.isInterface() | 
|  | ? resolveMethodOnInterfaceLegacy(clazz, methodSignature) | 
|  | : resolveMethodOnClassLegacy(clazz, methodSignature); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnLegacy( | 
|  | DexType holder, DexMethod method, boolean isInterface) { | 
|  | assert checkIfObsolete(); | 
|  | return isInterface | 
|  | ? resolveMethodOnInterfaceLegacy(holder, method) | 
|  | : resolveMethodOnClassLegacy(holder, method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassHolderLegacy(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClassLegacy(method.getHolderType(), method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy(DexType holder, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClassLegacy(holder, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy( | 
|  | DexType holder, DexMethodSignature signature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClassLegacy(holder, signature.getProto(), signature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy( | 
|  | DexType holder, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMethodOnClass(holder, proto, name); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClassLegacy(clazz, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy( | 
|  | DexClass clazz, DexMethodSignature signature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClassLegacy(clazz, signature.getProto(), signature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassLegacy( | 
|  | DexClass clazz, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMethodOnClass(clazz, proto, name); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceHolderLegacy(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterfaceLegacy(method.getHolderType(), method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceLegacy(DexType holder, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMethodOnInterface(holder, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceLegacy(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterfaceLegacy(clazz, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceLegacy( | 
|  | DexClass clazz, DexMethodSignature methodSignature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterfaceLegacy( | 
|  | clazz, methodSignature.getProto(), methodSignature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceLegacy( | 
|  | DexClass clazz, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.createLegacy(this::definitionFor, dexItemFactory()) | 
|  | .resolveMethodOnInterface(clazz, proto, name); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * This method will query the definition of the holder to decide on which resolution to use. | 
|  | * | 
|  | * <p>This is to overcome the shortcoming of the DEX file format that does not allow to encode the | 
|  | * kind of a method reference. | 
|  | */ | 
|  | public MethodResolutionResult unsafeResolveMethodDueToDexFormat(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.create( | 
|  | this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) | 
|  | .unsafeResolveMethodDueToDexFormat(method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethod(DexMethod invokedMethod, boolean isInterface) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOn(invokedMethod.getHolderType(), invokedMethod, isInterface); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOn( | 
|  | DexType holder, DexMethod method, boolean isInterface) { | 
|  | assert checkIfObsolete(); | 
|  | return isInterface | 
|  | ? resolveMethodOnInterface(holder, method) | 
|  | : resolveMethodOnClass(holder, method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClassHolder(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClass(method.getHolderType(), method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass(DexType holder, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClass(holder, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass(DexType holder, DexMethodSignature signature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClass(holder, signature.getProto(), signature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass( | 
|  | DexType holder, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.create( | 
|  | this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) | 
|  | .resolveMethodOnClass(holder, proto, name); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClass(clazz, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass(DexClass clazz, DexMethodSignature signature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnClass(clazz, signature.getProto(), signature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnClass( | 
|  | DexClass clazz, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.create( | 
|  | this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) | 
|  | .resolveMethodOnClass(clazz, proto, name); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterfaceHolder(DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterface(method.getHolderType(), method); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterface(DexType holder, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.create( | 
|  | this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) | 
|  | .resolveMethodOnInterface(holder, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterface(DexClass clazz, DexMethod method) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterface(clazz, method.getProto(), method.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterface( | 
|  | DexClass clazz, DexMethodSignature methodSignature) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveMethodOnInterface(clazz, methodSignature.getProto(), methodSignature.getName()); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult resolveMethodOnInterface( | 
|  | DexClass clazz, DexProto proto, DexString name) { | 
|  | assert checkIfObsolete(); | 
|  | return MethodResolution.create( | 
|  | this::contextIndependentDefinitionForWithResolutionResult, dexItemFactory()) | 
|  | .resolveMethodOnInterface(clazz, proto, name); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Implements resolution of a field descriptor against the holder of the field. See also {@link | 
|  | * #resolveFieldOn}. | 
|  | */ | 
|  | public FieldResolutionResult resolveField(DexField field) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveFieldOn(field.holder, field); | 
|  | } | 
|  |  | 
|  | /** Intentionally drops {@param context} since this is only needed in D8. */ | 
|  | @Override | 
|  | public FieldResolutionResult resolveFieldOn(DexType type, DexField field, ProgramMethod context) { | 
|  | assert checkIfObsolete(); | 
|  | return resolveFieldOn(type, field); | 
|  | } | 
|  |  | 
|  | // Keep as instance methods to ensure that one needs AppInfoWithClassHierarchy to do resolution. | 
|  | public FieldResolutionResult resolveFieldOn(DexType type, DexField field) { | 
|  | assert checkIfObsolete(); | 
|  | return new FieldResolution(this).resolveFieldOn(type, field); | 
|  | } | 
|  |  | 
|  | // Keep as instance methods to ensure that one needs AppInfoWithClassHierarchy to do resolution. | 
|  | public FieldResolutionResult resolveFieldOn(DexClass clazz, DexField field) { | 
|  | assert checkIfObsolete(); | 
|  | return new FieldResolution(this).resolveFieldOn(clazz, field); | 
|  | } | 
|  | } |