| // 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.horizontalclassmerging.HorizontalClassMerger; |
| 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 notifyHorizontalClassMergerFinished( |
| HorizontalClassMerger.Mode horizontalClassMergerMode) { |
| // Intentionally empty. |
| } |
| |
| 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("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 DexEncodedMethod lookupStaticTarget( |
| DexMethod method, |
| DexProgramClass context, |
| AppView<? extends AppInfoWithClassHierarchy> appView) { |
| return lookupStaticTarget(method, context, appView, appView.appInfo()); |
| } |
| |
| public DexEncodedMethod 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 DexEncodedMethod lookupStaticTarget( |
| DexMethod method, |
| ProgramMethod context, |
| AppView<? extends AppInfoWithClassHierarchy> appView) { |
| return lookupStaticTarget(method, context.getHolder(), appView); |
| } |
| |
| public DexEncodedMethod 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 DexEncodedMethod lookupDirectTarget( |
| DexMethod method, |
| DexProgramClass context, |
| AppView<? extends AppInfoWithClassHierarchy> appView) { |
| return lookupDirectTarget(method, context, appView, appView.appInfo()); |
| } |
| |
| public DexEncodedMethod 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 DexEncodedMethod lookupDirectTarget( |
| DexMethod method, |
| ProgramMethod context, |
| AppView<? extends AppInfoWithClassHierarchy> appView) { |
| return lookupDirectTarget(method, context, appView, appView.appInfo()); |
| } |
| |
| public DexEncodedMethod 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); |
| } |
| } |