|  | // Copyright (c) 2019, 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.ConsumerUtils.emptyConsumer; | 
|  |  | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.graph.LookupResult.LookupResultSuccess; | 
|  | import com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState; | 
|  | import com.android.tools.r8.ir.desugar.LambdaDescriptor; | 
|  | import com.android.tools.r8.shaking.AppInfoWithLiveness; | 
|  | import com.android.tools.r8.shaking.InstantiatedObject; | 
|  | import com.android.tools.r8.utils.BooleanBox; | 
|  | import com.android.tools.r8.utils.Box; | 
|  | import com.android.tools.r8.utils.ConsumerUtils; | 
|  | import com.android.tools.r8.utils.ListUtils; | 
|  | import com.android.tools.r8.utils.OptionalBool; | 
|  | import com.android.tools.r8.utils.SetUtils; | 
|  | import com.google.common.collect.Iterables; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collection; | 
|  | import java.util.Collections; | 
|  | import java.util.List; | 
|  | import java.util.Set; | 
|  | import java.util.function.BiPredicate; | 
|  | import java.util.function.Consumer; | 
|  |  | 
|  | public abstract class MethodResolutionResult | 
|  | extends MemberResolutionResult<DexEncodedMethod, DexMethod> { | 
|  |  | 
|  | @Override | 
|  | public boolean isMethodResolutionResult() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public MethodResolutionResult asMethodResolutionResult() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns true if resolution succeeded *and* the resolved method has a known definition. | 
|  | * | 
|  | * <p>Note that {@code !isSingleResolution() && !isFailedResolution()} can be true. In that case | 
|  | * that resolution has succeeded, but the definition of the resolved method is unknown. In | 
|  | * particular this is the case for the clone() method on arrays. | 
|  | */ | 
|  | public boolean isSingleResolution() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** Returns non-null if isSingleResolution() is true, otherwise null. */ | 
|  | public SingleResolutionResult<?> asSingleResolution() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isSuccessfulMemberResolutionResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> | 
|  | asSuccessfulMemberResolutionResult() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isIncompatibleClassChangeErrorResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isNoSuchMethodResultDueToMultipleClassDefinitions() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean internalIsInstanceOfNoSuchMethodResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isIllegalAccessErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isClassNotFoundResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isArrayCloneMethodResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isMultiMethodResolutionResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public final void forEachMethodResolutionResult(Consumer<MethodResolutionResult> resultConsumer) { | 
|  | visitMethodResolutionResults(resultConsumer, resultConsumer, resultConsumer, resultConsumer); | 
|  | } | 
|  |  | 
|  | /** Returns non-null if isFailedResolution() is true, otherwise null. */ | 
|  | public FailedResolutionResult asFailedResolution() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public NoSuchMethodResult asNoSuchMethodResult() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public DexClass getResolvedHolder() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod getResolvedMethod() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** Short-hand to get the single resolution method if resolution finds it, null otherwise. */ | 
|  | public final DexEncodedMethod getSingleTarget() { | 
|  | return isSingleResolution() ? asSingleResolution().getResolvedMethod() : null; | 
|  | } | 
|  |  | 
|  | public DexClass getInitialResolutionHolder() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public ProgramMethod getResolvedProgramMethod() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod getResolutionPair() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public abstract OptionalBool isAccessibleForVirtualDispatchFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | public abstract boolean isVirtualTarget(); | 
|  |  | 
|  | /** Lookup the single target of an invoke-special on this resolution result if possible. */ | 
|  | public abstract DexClassAndMethod lookupInvokeSpecialTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | /** Lookup the single target of an invoke-super on this resolution result if possible. */ | 
|  | public abstract DexClassAndMethod lookupInvokeSuperTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | /** Lookup the single target of an invoke-direct on this resolution result if possible. */ | 
|  | public abstract DexEncodedMethod lookupInvokeDirectTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | /** Lookup the single target of an invoke-static on this resolution result if possible. */ | 
|  | public abstract DexEncodedMethod lookupInvokeStaticTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | public abstract LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | InstantiatedSubTypeInfo instantiatedInfo, | 
|  | PinnedPredicate pinnedPredicate); | 
|  |  | 
|  | public final LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, AppInfoWithLiveness appInfo) { | 
|  | return lookupVirtualDispatchTargets( | 
|  | context, appInfo, appInfo, appInfo::isPinnedNotProgramOrLibraryOverride); | 
|  | } | 
|  |  | 
|  | public abstract LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithLiveness appInfo, | 
|  | DexProgramClass refinedReceiverUpperBound, | 
|  | DexProgramClass refinedReceiverLowerBound); | 
|  |  | 
|  | public abstract LookupTarget lookupVirtualDispatchTarget( | 
|  | InstantiatedObject instance, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | public abstract LookupMethodTarget lookupVirtualDispatchTarget( | 
|  | DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo); | 
|  |  | 
|  | public abstract LookupTarget lookupVirtualDispatchTarget( | 
|  | LambdaDescriptor lambdaInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer); | 
|  |  | 
|  | public abstract void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer); | 
|  |  | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super MethodResolutionResult> resultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | visitMethodResolutionResults( | 
|  | resultConsumer, resultConsumer, resultConsumer, failedResolutionConsumer); | 
|  | } | 
|  |  | 
|  | public boolean hasProgramResult() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public SingleClasspathResolutionResult asSingleClasspathResolutionResult() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | protected SingleProgramResolutionResult asSingleProgramResolutionResult() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public static SingleResolutionResult<?> createSingleResolutionResult( | 
|  | DexClass initialResolutionHolder, DexClass holder, DexEncodedMethod definition) { | 
|  | if (holder.isLibraryClass()) { | 
|  | return new SingleLibraryResolutionResult( | 
|  | initialResolutionHolder, holder.asLibraryClass(), definition); | 
|  | } else if (holder.isClasspathClass()) { | 
|  | return new SingleClasspathResolutionResult( | 
|  | initialResolutionHolder, holder.asClasspathClass(), definition); | 
|  | } else { | 
|  | assert holder.isProgramClass(); | 
|  | return new SingleProgramResolutionResult( | 
|  | initialResolutionHolder, holder.asProgramClass(), definition); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Result for a resolution that succeeds with a known declaration/definition. */ | 
|  | public abstract static class SingleResolutionResult<T extends DexClass> | 
|  | extends MethodResolutionResult | 
|  | implements SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> { | 
|  | private final DexClass initialResolutionHolder; | 
|  | private final T resolvedHolder; | 
|  | private final DexEncodedMethod resolvedMethod; | 
|  |  | 
|  | public SingleResolutionResult( | 
|  | DexClass initialResolutionHolder, T resolvedHolder, DexEncodedMethod resolvedMethod) { | 
|  | assert initialResolutionHolder != null; | 
|  | assert resolvedHolder != null; | 
|  | assert resolvedMethod != null; | 
|  | assert resolvedHolder.type == resolvedMethod.getHolderType(); | 
|  | this.resolvedHolder = resolvedHolder; | 
|  | this.resolvedMethod = resolvedMethod; | 
|  | this.initialResolutionHolder = initialResolutionHolder; | 
|  | assert !resolvedMethod.isPrivateMethod() | 
|  | || initialResolutionHolder.type == resolvedMethod.getHolderType(); | 
|  | } | 
|  |  | 
|  | public abstract SingleResolutionResult<T> withInitialResolutionHolder( | 
|  | DexClass newInitialResolutionHolder); | 
|  |  | 
|  | @Override | 
|  | public DexClass getInitialResolutionHolder() { | 
|  | return initialResolutionHolder; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public T getResolvedHolder() { | 
|  | return resolvedHolder; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod getResolvedMember() { | 
|  | return resolvedMethod; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod getResolvedMethod() { | 
|  | return resolvedMethod; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ProgramMethod getResolvedProgramMethod() { | 
|  | return resolvedHolder.isProgramClass() | 
|  | ? new ProgramMethod(resolvedHolder.asProgramClass(), resolvedMethod) | 
|  | : null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod getResolutionPair() { | 
|  | return DexClassAndMethod.create(resolvedHolder, resolvedMethod); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isSingleResolution() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SingleResolutionResult<?> asSingleResolution() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isSuccessfulMemberResolutionResult() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SuccessfulMemberResolutionResult<DexEncodedMethod, DexMethod> | 
|  | asSuccessfulMemberResolutionResult() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | return AccessControl.isMemberAccessible(this, context, appInfo); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleForVirtualDispatchFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (resolvedMethod.isVirtualMethod()) { | 
|  | return isAccessibleFrom(context, appInfo); | 
|  | } | 
|  | return OptionalBool.FALSE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isVirtualTarget() { | 
|  | return resolvedMethod.isVirtualMethod(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * This is intended to model the actual behavior of invoke-special on a JVM. | 
|  | * | 
|  | * <p>See https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-6.html#jvms-6.5.invokespecial | 
|  | * and comments below for deviations due to diverging behavior on actual JVMs. | 
|  | */ | 
|  | @Override | 
|  | public DexClassAndMethod lookupInvokeSpecialTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | // If the resolution is non-accessible then no target exists. | 
|  | if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) { | 
|  | return internalInvokeSpecialOrSuper( | 
|  | context, appInfo, (sup, sub) -> isSuperclass(sup, sub, appInfo)); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private static DexClass definitionForHelper(AppInfoWithClassHierarchy appInfo, DexType type) { | 
|  | if (type == null) { | 
|  | return null; | 
|  | } | 
|  | ClassResolutionResult resolutionResult = | 
|  | appInfo.contextIndependentDefinitionForWithResolutionResult(type); | 
|  | return appInfo.options().lookupLibraryBeforeProgram | 
|  | ? resolutionResult.toSingleClassWithLibraryOverProgram() | 
|  | : resolutionResult.toSingleClassWithProgramOverLibrary(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup the target of an invoke-super. | 
|  | * | 
|  | * <p>This will return the target iff the resolution succeeded and the target is valid (i.e., | 
|  | * non-static and non-initializer) and accessible from {@code context}. | 
|  | * | 
|  | * <p>Additionally, this will also verify that the invoke-super is valid, i.e., it is on the a | 
|  | * super type of the current context. Any invoke-special targeting the same type should have | 
|  | * been mapped to an invoke-direct, but could change due to merging so we need to still allow | 
|  | * the context to be equal to the targeted (symbolically referenced) type. | 
|  | * | 
|  | * @param context Class the invoke is contained in, i.e., the holder of the caller. | 
|  | * @param appInfo Application info. | 
|  | * @return The actual target for the invoke-super or {@code null} if no valid target is found. | 
|  | */ | 
|  | @Override | 
|  | public DexClassAndMethod lookupInvokeSuperTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (resolvedMethod.isInstanceInitializer() | 
|  | || (initialResolutionHolder != context | 
|  | && !isSuperclass(initialResolutionHolder, context, appInfo))) { | 
|  | // If the target is <init> or not on a super class then the call is invalid. | 
|  | return null; | 
|  | } | 
|  | if (isAccessibleFrom(context, appInfo).isPossiblyTrue()) { | 
|  | return internalInvokeSpecialOrSuper(context, appInfo, (sup, sub) -> true); | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup the target of an invoke-static. | 
|  | * | 
|  | * <p>This method will resolve the method on the holder and only return a non-null value if the | 
|  | * result of resolution was a static, non-abstract method. | 
|  | * | 
|  | * @param context Class the invoke is contained in, i.e., the holder of the caller. | 
|  | * @param appInfo Application info. | 
|  | * @return The actual target or {@code null} if none found. | 
|  | */ | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeStaticTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (isAccessibleFrom(context, appInfo).isFalse()) { | 
|  | return null; | 
|  | } | 
|  | if (resolvedMethod.isStatic()) { | 
|  | return resolvedMethod; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Lookup direct method following the super chain from the holder of {@code method}. | 
|  | * | 
|  | * <p>This method will lookup private and constructor methods. | 
|  | * | 
|  | * @param context Class the invoke is contained in, i.e., the holder of the caller. * @param | 
|  | *     appInfo Application info. | 
|  | * @return The actual target or {@code null} if none found. | 
|  | */ | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeDirectTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (isAccessibleFrom(context, appInfo).isFalse()) { | 
|  | return null; | 
|  | } | 
|  | if (resolvedMethod.isDirectMethod()) { | 
|  | return resolvedMethod; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private DexClassAndMethod internalInvokeSpecialOrSuper( | 
|  | DexProgramClass context, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | BiPredicate<DexClass, DexClass> isSuperclass) { | 
|  |  | 
|  | // Statics cannot be targeted by invoke-special/super. | 
|  | if (getResolvedMethod().isStatic()) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | if (getResolvedHolder().isInterface() && getResolvedMethod().isPrivate()) { | 
|  | return getResolutionPair(); | 
|  | } | 
|  |  | 
|  | // The symbolic reference is the holder type that resolution was initiated at. | 
|  | DexClass symbolicReference = initialResolutionHolder; | 
|  |  | 
|  | // First part of the spec is to determine the starting point for lookup for invoke special. | 
|  | // Notice that the specification indicates that the immediate super type should | 
|  | // be used when three items hold, the second being: | 
|  | //   is-class(sym-ref) => is-super(sym-ref, context) | 
|  | // in the case of an interface that is trivially satisfied, which would lead the initial type | 
|  | // to be java.lang.Object. However in practice the lookup appears to start at the symbolic | 
|  | // reference in the case of interfaces, so the second condition should likely be interpreted: | 
|  | //   is-class(sym-ref) *and* is-super(sym-ref, context). | 
|  | final DexClass initialType; | 
|  | if (!resolvedMethod.isInstanceInitializer() | 
|  | && !symbolicReference.isInterface() | 
|  | && isSuperclass.test(symbolicReference, context)) { | 
|  | // If reference is a super type of the context then search starts at the immediate super. | 
|  | initialType = definitionForHelper(appInfo, context.superType); | 
|  | } else { | 
|  | // Otherwise it starts at the reference itself. | 
|  | initialType = symbolicReference; | 
|  | } | 
|  | // Abort if for some reason the starting point could not be found. | 
|  | if (initialType == null) { | 
|  | return null; | 
|  | } | 
|  | // 1-3. Search the initial class and its supers in order for a matching instance method. | 
|  | DexMethod method = getResolvedMethod().getReference(); | 
|  | DexClassAndMethod target = null; | 
|  | DexClass current = initialType; | 
|  | while (current != null) { | 
|  | target = current.lookupClassMethod(method); | 
|  | if (target != null) { | 
|  | break; | 
|  | } | 
|  | current = definitionForHelper(appInfo, current.superType); | 
|  | } | 
|  | // 4. Otherwise, it is the single maximally specific method: | 
|  | if (target == null) { | 
|  | target = appInfo.lookupMaximallySpecificMethod(initialType, method); | 
|  | } | 
|  | if (target == null) { | 
|  | return null; | 
|  | } | 
|  | // Linking exceptions: | 
|  | // A non-instance method throws IncompatibleClassChangeError. | 
|  | if (target.getAccessFlags().isStatic()) { | 
|  | return null; | 
|  | } | 
|  | // An instance initializer that is not to the symbolic reference throws NoSuchMethodError. | 
|  | // It appears as if this check is also in place for non-initializer methods too. | 
|  | // See NestInvokeSpecialMethodAccessWithIntermediateTest. | 
|  | if ((target.getDefinition().isInstanceInitializer() || target.getAccessFlags().isPrivate()) | 
|  | && target.getHolderType() != symbolicReference.type) { | 
|  | return null; | 
|  | } | 
|  | // Runtime exceptions: | 
|  | // An abstract method throws AbstractMethodError. | 
|  | if (target.getAccessFlags().isAbstract()) { | 
|  | return null; | 
|  | } | 
|  | return target; | 
|  | } | 
|  |  | 
|  | private static boolean isSuperclass( | 
|  | DexClass sup, DexClass sub, AppInfoWithClassHierarchy appInfo) { | 
|  | return appInfo.isStrictSubtypeOf(sub.type, sup.type); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | InstantiatedSubTypeInfo instantiatedInfo, | 
|  | PinnedPredicate pinnedPredicate) { | 
|  | // Check that the initial resolution holder is accessible from the context. | 
|  | assert appInfo.isSubtype(initialResolutionHolder.type, resolvedHolder.type) | 
|  | : initialResolutionHolder.type + " is not a subtype of " + resolvedHolder.type; | 
|  | if (context != null && isAccessibleFrom(context, appInfo).isFalse()) { | 
|  | return LookupResult.createFailedResult(); | 
|  | } | 
|  | if (resolvedMethod.isPrivateMethod()) { | 
|  | // If the resolved reference is private there is no dispatch. | 
|  | // This is assuming that the method is accessible, which implies self/nest access. | 
|  | // Only include if the target has code or is native. | 
|  | boolean isIncomplete = | 
|  | pinnedPredicate.isPinned(resolvedHolder) && pinnedPredicate.isPinned(resolvedMethod); | 
|  | DexClassAndMethod resolutionPair = getResolutionPair(); | 
|  | return LookupResult.createResult( | 
|  | Collections.singletonMap(resolutionPair.getReference(), resolutionPair), | 
|  | Collections.emptyList(), | 
|  | Collections.emptyList(), | 
|  | isIncomplete | 
|  | ? LookupResultCollectionState.Incomplete | 
|  | : LookupResultCollectionState.Complete); | 
|  | } | 
|  | assert resolvedMethod.isNonPrivateVirtualMethod(); | 
|  | LookupResultSuccess.Builder resultBuilder = LookupResultSuccess.builder(); | 
|  | LookupCompletenessHelper incompleteness = new LookupCompletenessHelper(pinnedPredicate); | 
|  | instantiatedInfo.forEachInstantiatedSubType( | 
|  | initialResolutionHolder.type, | 
|  | subClass -> { | 
|  | incompleteness.checkClass(subClass); | 
|  | LookupMethodTarget lookupTarget = | 
|  | lookupVirtualDispatchTarget( | 
|  | subClass, | 
|  | appInfo, | 
|  | resolvedHolder.type, | 
|  | resultBuilder::addTypeCausingFailure, | 
|  | resultBuilder::addMethodCausingFailure); | 
|  | if (lookupTarget != null) { | 
|  | incompleteness.checkDexClassAndMethod(lookupTarget); | 
|  | addVirtualDispatchTarget(lookupTarget, resolvedHolder.isInterface(), resultBuilder); | 
|  | } | 
|  | }, | 
|  | lambda -> { | 
|  | assert resolvedHolder.isInterface() | 
|  | || resolvedHolder.type == appInfo.dexItemFactory().objectType; | 
|  | LookupTarget target = | 
|  | lookupVirtualDispatchTarget( | 
|  | lambda, | 
|  | appInfo, | 
|  | resultBuilder::addTypeCausingFailure, | 
|  | resultBuilder::addMethodCausingFailure); | 
|  | if (target != null) { | 
|  | if (target.isLambdaTarget()) { | 
|  | resultBuilder.addLambdaTarget(target.asLambdaTarget()); | 
|  | } else { | 
|  | addVirtualDispatchTarget( | 
|  | target.asMethodTarget(), resolvedHolder.isInterface(), resultBuilder); | 
|  | } | 
|  | } | 
|  | }); | 
|  | return resultBuilder | 
|  | .setState(incompleteness.computeCollectionState(resolvedMethod.getReference(), appInfo)) | 
|  | .build(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithLiveness appInfo, | 
|  | DexProgramClass refinedReceiverUpperBound, | 
|  | DexProgramClass refinedReceiverLowerBound) { | 
|  | assert refinedReceiverUpperBound != null; | 
|  | assert appInfo.isSubtype(refinedReceiverUpperBound.type, initialResolutionHolder.type); | 
|  | assert refinedReceiverLowerBound == null | 
|  | || appInfo.isSubtype(refinedReceiverLowerBound.type, refinedReceiverUpperBound.type); | 
|  | // TODO(b/148769279): Remove the check for hasInstantiatedLambdas. | 
|  | Box<Boolean> hasInstantiatedLambdas = new Box<>(false); | 
|  | InstantiatedSubTypeInfo instantiatedSubTypeInfo = | 
|  | instantiatedSubTypeInfoForInstantiatedType( | 
|  | appInfo, | 
|  | refinedReceiverUpperBound, | 
|  | refinedReceiverLowerBound, | 
|  | hasInstantiatedLambdas); | 
|  | LookupResult lookupResult = | 
|  | lookupVirtualDispatchTargets( | 
|  | context, | 
|  | appInfo, | 
|  | instantiatedSubTypeInfo, | 
|  | appInfo::isPinnedNotProgramOrLibraryOverride); | 
|  | if (hasInstantiatedLambdas.get() && lookupResult.isLookupResultSuccess()) { | 
|  | lookupResult.asLookupResultSuccess().setIncomplete(); | 
|  | } | 
|  | return lookupResult; | 
|  | } | 
|  |  | 
|  | private InstantiatedSubTypeInfo instantiatedSubTypeInfoForInstantiatedType( | 
|  | AppInfoWithLiveness appInfo, | 
|  | DexProgramClass refinedReceiverUpperBound, | 
|  | DexProgramClass refinedReceiverLowerBound, | 
|  | Box<Boolean> hasInstantiatedLambdas) { | 
|  | return (ignored, subTypeConsumer, callSiteConsumer) -> { | 
|  | Consumer<DexProgramClass> lambdaInstantiatedConsumer = | 
|  | subType -> { | 
|  | subTypeConsumer.accept(subType); | 
|  | if (appInfo.isInstantiatedInterface(subType)) { | 
|  | hasInstantiatedLambdas.set(true); | 
|  | } | 
|  | }; | 
|  | if (refinedReceiverLowerBound == null) { | 
|  | appInfo.forEachInstantiatedSubType( | 
|  | refinedReceiverUpperBound.type, lambdaInstantiatedConsumer, callSiteConsumer); | 
|  | } else { | 
|  | appInfo.forEachInstantiatedSubTypeInChain( | 
|  | refinedReceiverUpperBound, | 
|  | refinedReceiverLowerBound, | 
|  | lambdaInstantiatedConsumer, | 
|  | callSiteConsumer); | 
|  | } | 
|  | }; | 
|  | } | 
|  |  | 
|  | private static void addVirtualDispatchTarget( | 
|  | LookupMethodTarget target, | 
|  | boolean holderIsInterface, | 
|  | LookupResultSuccess.Builder resultBuilder) { | 
|  | assert target.isMethodTarget(); | 
|  | DexEncodedMethod targetMethod = target.asMethodTarget().getDefinition(); | 
|  | assert !targetMethod.isPrivateMethod(); | 
|  | if (holderIsInterface) { | 
|  | // Add default interface methods to the list of targets. | 
|  | // | 
|  | // This helps to make sure we take into account synthesized lambda classes | 
|  | // that we are not aware of. Like in the following example, we know that all | 
|  | // classes, XX in this case, override B::bar(), but there are also synthesized | 
|  | // classes for lambda which don't, so we still need default method to be live. | 
|  | // | 
|  | //   public static void main(String[] args) { | 
|  | //     X x = () -> {}; | 
|  | //     x.bar(); | 
|  | //   } | 
|  | // | 
|  | //   interface X { | 
|  | //     void foo(); | 
|  | //     default void bar() { } | 
|  | //   } | 
|  | // | 
|  | //   class XX implements X { | 
|  | //     public void foo() { } | 
|  | //     public void bar() { } | 
|  | //   } | 
|  | // | 
|  | if (targetMethod.isDefaultMethod()) { | 
|  | resultBuilder.addMethodTarget(target); | 
|  | } | 
|  | // Default methods are looked up when looking at a specific subtype that does not override | 
|  | // them. Otherwise, we would look up default methods that are actually never used. | 
|  | // However, we have to add bridge methods, otherwise we can remove a bridge that will be | 
|  | // used. | 
|  | if (!targetMethod.accessFlags.isAbstract() && targetMethod.accessFlags.isBridge()) { | 
|  | resultBuilder.addMethodTarget(target); | 
|  | } | 
|  | } else { | 
|  | resultBuilder.addMethodTarget(target); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * This implements the logic for the actual method selection for a virtual target, according to | 
|  | * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokevirtual where | 
|  | * we have an object ref on the stack. | 
|  | */ | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) { | 
|  | return instance.isClass() | 
|  | ? lookupVirtualDispatchTarget(instance.asClass(), appInfo) | 
|  | : lookupVirtualDispatchTarget( | 
|  | instance.asLambda(), appInfo, emptyConsumer(), emptyConsumer()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupMethodTarget lookupVirtualDispatchTarget( | 
|  | DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { | 
|  | return lookupVirtualDispatchTarget( | 
|  | dynamicInstance, appInfo, initialResolutionHolder.type, emptyConsumer(), emptyConsumer()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | LambdaDescriptor lambdaInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | if (lambdaInstance.getMainMethod().match(resolvedMethod)) { | 
|  | DexMethod methodReference = lambdaInstance.implHandle.asMethod(); | 
|  | DexClass holder = definitionForHelper(appInfo, methodReference.getHolderType()); | 
|  | DexClassAndMethod method = methodReference.lookupMemberOnClass(holder); | 
|  | if (method == null) { | 
|  | // The targeted method might not exist, eg, Throwable.addSuppressed in an old library. | 
|  | return null; | 
|  | } | 
|  | return new LookupLambdaTarget(lambdaInstance, method); | 
|  | } | 
|  | return lookupMaximallySpecificDispatchTarget( | 
|  | lambdaInstance, appInfo, typeCausingFailureConsumer, methodCausingFailureConsumer); | 
|  | } | 
|  |  | 
|  | private LookupMethodTarget lookupVirtualDispatchTarget( | 
|  | DexClass dynamicInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | DexType resolutionHolder, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | assert appInfo.isSubtype(dynamicInstance.type, resolutionHolder) | 
|  | : dynamicInstance.type + " is not a subtype of " + resolutionHolder; | 
|  | // TODO(b/148591377): Enable this assertion. | 
|  | // The dynamic type cannot be an interface. | 
|  | // assert !dynamicInstance.isInterface(); | 
|  | DexClassAndMethod initialResolutionPair = getResolutionPair(); | 
|  | if (resolvedMethod.isPrivateMethod()) { | 
|  | // If the resolved reference is private there is no dispatch. | 
|  | // This is assuming that the method is accessible, which implies self/nest access. | 
|  | return initialResolutionPair; | 
|  | } | 
|  | boolean allowPackageBlocked = resolvedMethod.accessFlags.isPackagePrivate(); | 
|  | DexClass current = dynamicInstance; | 
|  | DexClassAndMethod overrideTarget = initialResolutionPair; | 
|  | while (current != null) { | 
|  | DexEncodedMethod candidate = | 
|  | lookupOverrideCandidate(overrideTarget.getDefinition(), current); | 
|  | if (candidate == DexEncodedMethod.SENTINEL && allowPackageBlocked) { | 
|  | overrideTarget = findWideningOverride(initialResolutionPair, current, appInfo); | 
|  | allowPackageBlocked = false; | 
|  | continue; | 
|  | } | 
|  | if (candidate == null || candidate == DexEncodedMethod.SENTINEL) { | 
|  | // We cannot find a target above the resolved method. | 
|  | if (current.type == overrideTarget.getHolderType()) { | 
|  | return null; | 
|  | } | 
|  | current = definitionForHelper(appInfo, current.superType); | 
|  | continue; | 
|  | } | 
|  | DexClassAndMethod target = DexClassAndMethod.create(current, candidate); | 
|  | return overrideTarget != initialResolutionPair | 
|  | ? new LookupMethodTargetWithAccessOverride(target, overrideTarget) | 
|  | : target; | 
|  | } | 
|  | // If we have not found a candidate and the holder is not an interface it must be because the | 
|  | // class is missing. | 
|  | if (!resolvedHolder.isInterface()) { | 
|  | return null; | 
|  | } | 
|  | return lookupMaximallySpecificDispatchTarget( | 
|  | dynamicInstance, appInfo, typeCausingFailureConsumer, methodCausingFailureConsumer); | 
|  | } | 
|  |  | 
|  | private DexClassAndMethod lookupMaximallySpecificDispatchTarget( | 
|  | DexClass dynamicInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | MethodResolutionResult maximallySpecificResolutionResult = | 
|  | appInfo.resolveMaximallySpecificTarget(dynamicInstance, resolvedMethod.getReference()); | 
|  | if (maximallySpecificResolutionResult.isSingleResolution()) { | 
|  | return maximallySpecificResolutionResult.getResolutionPair(); | 
|  | } | 
|  | if (maximallySpecificResolutionResult.isFailedResolution()) { | 
|  | maximallySpecificResolutionResult | 
|  | .asFailedResolution() | 
|  | .forEachFailureDependency(typeCausingFailureConsumer, methodCausingFailureConsumer); | 
|  | return null; | 
|  | } | 
|  | assert maximallySpecificResolutionResult.isArrayCloneMethodResult(); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private DexClassAndMethod lookupMaximallySpecificDispatchTarget( | 
|  | LambdaDescriptor lambdaDescriptor, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | MethodResolutionResult maximallySpecificResolutionResult = | 
|  | appInfo.resolveMaximallySpecificTarget(lambdaDescriptor, resolvedMethod.getReference()); | 
|  | if (maximallySpecificResolutionResult.isSingleResolution()) { | 
|  | return maximallySpecificResolutionResult.getResolutionPair(); | 
|  | } | 
|  | if (maximallySpecificResolutionResult.isFailedResolution()) { | 
|  | maximallySpecificResolutionResult | 
|  | .asFailedResolution() | 
|  | .forEachFailureDependency(typeCausingFailureConsumer, methodCausingFailureConsumer); | 
|  | return null; | 
|  | } | 
|  | assert maximallySpecificResolutionResult.isArrayCloneMethodResult(); | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * C contains a declaration for an instance method m that overrides (§5.4.5) the resolved | 
|  | * method, then m is the method to be invoked. If the candidate is not a valid override, we | 
|  | * return sentinel to indicate that we have to search for a method that is widening access | 
|  | * inside the package. | 
|  | */ | 
|  | private static DexEncodedMethod lookupOverrideCandidate( | 
|  | DexEncodedMethod method, DexClass clazz) { | 
|  | DexEncodedMethod candidate = clazz.lookupVirtualMethod(method.getReference()); | 
|  | assert candidate == null || !candidate.isPrivateMethod(); | 
|  | if (candidate != null) { | 
|  | return isOverriding(method, candidate) ? candidate : DexEncodedMethod.SENTINEL; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | private static DexClassAndMethod findWideningOverride( | 
|  | DexClassAndMethod resolvedMethod, DexClass clazz, AppInfoWithClassHierarchy appInfo) { | 
|  | // Otherwise, lookup to first override that is distinct from resolvedMethod. | 
|  | assert resolvedMethod.getDefinition().accessFlags.isPackagePrivate(); | 
|  | while (clazz.superType != null) { | 
|  | clazz = definitionForHelper(appInfo, clazz.superType); | 
|  | if (clazz == null) { | 
|  | return resolvedMethod; | 
|  | } | 
|  | DexEncodedMethod otherOverride = clazz.lookupVirtualMethod(resolvedMethod.getReference()); | 
|  | if (otherOverride != null | 
|  | && isOverriding(resolvedMethod.getDefinition(), otherOverride) | 
|  | && (otherOverride.accessFlags.isPublic() || otherOverride.accessFlags.isProtected())) { | 
|  | assert resolvedMethod.getDefinition() != otherOverride; | 
|  | return DexClassAndMethod.create(clazz, otherOverride); | 
|  | } | 
|  | } | 
|  | return resolvedMethod; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Implementation of method overriding according to the jvm specification | 
|  | * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.5 | 
|  | * | 
|  | * <p>The implementation assumes that the holder of the candidate is a subtype of the holder of | 
|  | * the resolved method. It also assumes that resolvedMethod is the actual method to find a | 
|  | * lookup for (that is, it is either mA or m'). | 
|  | */ | 
|  | public static boolean isOverriding( | 
|  | DexEncodedMethod resolvedMethod, DexEncodedMethod candidate) { | 
|  | assert resolvedMethod.getReference().match(candidate.getReference()); | 
|  | assert !candidate.isPrivateMethod(); | 
|  | if (resolvedMethod.accessFlags.isPublic() || resolvedMethod.accessFlags.isProtected()) { | 
|  | return true; | 
|  | } | 
|  | // For package private methods, a valid override has to be inside the package. | 
|  | assert resolvedMethod.accessFlags.isPackagePrivate(); | 
|  | return resolvedMethod.getHolderType().isSamePackage(candidate.getHolderType()); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class SingleProgramResolutionResult | 
|  | extends SingleResolutionResult<DexProgramClass> { | 
|  |  | 
|  | public SingleProgramResolutionResult( | 
|  | DexClass initialResolutionHolder, | 
|  | DexProgramClass resolvedHolder, | 
|  | DexEncodedMethod resolvedMethod) { | 
|  | super(initialResolutionHolder, resolvedHolder, resolvedMethod); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SingleResolutionResult<DexProgramClass> withInitialResolutionHolder( | 
|  | DexClass newInitialResolutionHolder) { | 
|  | return newInitialResolutionHolder != getInitialResolutionHolder() | 
|  | ? new SingleProgramResolutionResult( | 
|  | newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod()) | 
|  | : this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | programOrClasspathConsumer.accept(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasProgramResult() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | protected SingleProgramResolutionResult asSingleProgramResolutionResult() { | 
|  | return this; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class SingleClasspathResolutionResult | 
|  | extends SingleResolutionResult<DexClasspathClass> { | 
|  |  | 
|  | public SingleClasspathResolutionResult( | 
|  | DexClass initialResolutionHolder, | 
|  | DexClasspathClass resolvedHolder, | 
|  | DexEncodedMethod resolvedMethod) { | 
|  | super(initialResolutionHolder, resolvedHolder, resolvedMethod); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SingleClasspathResolutionResult withInitialResolutionHolder( | 
|  | DexClass newInitialResolutionHolder) { | 
|  | return newInitialResolutionHolder != getInitialResolutionHolder() | 
|  | ? new SingleClasspathResolutionResult( | 
|  | newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod()) | 
|  | : this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | programOrClasspathConsumer.accept(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SingleClasspathResolutionResult asSingleClasspathResolutionResult() { | 
|  | return this; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class SingleLibraryResolutionResult | 
|  | extends SingleResolutionResult<DexLibraryClass> { | 
|  |  | 
|  | public SingleLibraryResolutionResult( | 
|  | DexClass initialResolutionHolder, | 
|  | DexLibraryClass resolvedHolder, | 
|  | DexEncodedMethod resolvedMethod) { | 
|  | super(initialResolutionHolder, resolvedHolder, resolvedMethod); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SingleLibraryResolutionResult withInitialResolutionHolder( | 
|  | DexClass newInitialResolutionHolder) { | 
|  | return newInitialResolutionHolder != getInitialResolutionHolder() | 
|  | ? new SingleLibraryResolutionResult( | 
|  | newInitialResolutionHolder, getResolvedHolder(), getResolvedMethod()) | 
|  | : this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | libraryResultConsumer.accept(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | abstract static class EmptyResult extends MethodResolutionResult { | 
|  |  | 
|  | @Override | 
|  | public final DexClassAndMethod lookupInvokeSpecialTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod lookupInvokeSuperTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeStaticTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeDirectTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | InstantiatedSubTypeInfo instantiatedInfo, | 
|  | PinnedPredicate pinnedPredicate) { | 
|  | return LookupResult.getIncompleteEmptyResult(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithLiveness appInfo, | 
|  | DexProgramClass refinedReceiverUpperBound, | 
|  | DexProgramClass refinedReceiverLowerBound) { | 
|  | return LookupResult.getIncompleteEmptyResult(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupMethodTarget lookupVirtualDispatchTarget( | 
|  | DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | LambdaDescriptor lambdaInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | return null; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Singleton result for the special case resolving the array clone() method. */ | 
|  | public static class ArrayCloneMethodResult extends EmptyResult { | 
|  |  | 
|  | static final ArrayCloneMethodResult INSTANCE = new ArrayCloneMethodResult(); | 
|  |  | 
|  | private ArrayCloneMethodResult() { | 
|  | // Intentionally left empty. | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | return OptionalBool.TRUE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleForVirtualDispatchFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | return OptionalBool.TRUE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isVirtualTarget() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | cloneResultConsumer.accept(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isArrayCloneMethodResult() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Base class for all types of failed resolutions. */ | 
|  | public abstract static class FailedResolutionResult extends EmptyResult { | 
|  |  | 
|  | protected final Collection<DexType> typesCausingError; | 
|  |  | 
|  | private FailedResolutionResult(Collection<DexType> typesCausingError) { | 
|  | this.typesCausingError = typesCausingError; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isFailedResolution() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public FailedResolutionResult asFailedResolution() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public void forEachFailureDependency( | 
|  | Consumer<DexType> typesCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | if (typesCausingError != null) { | 
|  | typesCausingError.forEach(typesCausingFailureConsumer); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | return OptionalBool.FALSE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleForVirtualDispatchFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | return OptionalBool.FALSE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isVirtualTarget() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean hasMethodsCausingError() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | failedResolutionConsumer.accept(this); | 
|  | } | 
|  |  | 
|  | public boolean hasTypesOrMethodsCausingError() { | 
|  | return (typesCausingError != null && !typesCausingError.isEmpty()) | 
|  | || hasMethodsCausingError(); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class ClassNotFoundResult extends FailedResolutionResult { | 
|  | static final ClassNotFoundResult INSTANCE = new ClassNotFoundResult(); | 
|  |  | 
|  | private ClassNotFoundResult() { | 
|  | super(null); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isClassNotFoundResult() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | public abstract static class FailedResolutionWithCausingMethods extends FailedResolutionResult { | 
|  |  | 
|  | private final Collection<DexEncodedMethod> methodsCausingError; | 
|  |  | 
|  | private FailedResolutionWithCausingMethods(Collection<DexEncodedMethod> methodsCausingError) { | 
|  | super(ListUtils.map(methodsCausingError, DexEncodedMember::getHolderType)); | 
|  | this.methodsCausingError = methodsCausingError; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void forEachFailureDependency( | 
|  | Consumer<DexType> typesCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | super.forEachFailureDependency(typesCausingFailureConsumer, methodCausingFailureConsumer); | 
|  | this.methodsCausingError.forEach(methodCausingFailureConsumer); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasMethodsCausingError() { | 
|  | return methodsCausingError.size() > 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class IncompatibleClassResult extends FailedResolutionWithCausingMethods { | 
|  | static final IncompatibleClassResult INSTANCE = | 
|  | new IncompatibleClassResult(Collections.emptyList()); | 
|  |  | 
|  | private IncompatibleClassResult(Collection<DexEncodedMethod> methodsCausingError) { | 
|  | super(methodsCausingError); | 
|  | } | 
|  |  | 
|  | static IncompatibleClassResult create(Collection<DexEncodedMethod> methodsCausingError) { | 
|  | return methodsCausingError.isEmpty() | 
|  | ? INSTANCE | 
|  | : new IncompatibleClassResult(methodsCausingError); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isIncompatibleClassChangeErrorResult() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class NoSuchMethodResult extends FailedResolutionResult { | 
|  |  | 
|  | static final NoSuchMethodResult INSTANCE = new NoSuchMethodResult(); | 
|  |  | 
|  | private NoSuchMethodResult() { | 
|  | super(null); | 
|  | } | 
|  |  | 
|  | protected NoSuchMethodResult(Collection<DexType> typesCausingError) { | 
|  | super(typesCausingError); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean internalIsInstanceOfNoSuchMethodResult() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public NoSuchMethodResult asNoSuchMethodResult() { | 
|  | return this; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class NoSuchMethodResultDueToMultipleClassDefinitions extends NoSuchMethodResult { | 
|  |  | 
|  | public NoSuchMethodResultDueToMultipleClassDefinitions(Collection<DexType> typesCausingError) { | 
|  | super(typesCausingError); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isNoSuchMethodResultDueToMultipleClassDefinitions() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | static class IllegalAccessOrNoSuchMethodResult extends FailedResolutionWithCausingMethods { | 
|  |  | 
|  | private final DexClass initialResolutionHolder; | 
|  |  | 
|  | public IllegalAccessOrNoSuchMethodResult( | 
|  | DexClass initialResolutionHolder, Collection<DexEncodedMethod> methodsCausingError) { | 
|  | super(methodsCausingError); | 
|  | this.initialResolutionHolder = initialResolutionHolder; | 
|  | } | 
|  |  | 
|  | public IllegalAccessOrNoSuchMethodResult( | 
|  | DexClass initialResolutionHolder, DexEncodedMethod methodCausingError) { | 
|  | this(initialResolutionHolder, Collections.singletonList(methodCausingError)); | 
|  | assert methodCausingError != null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isIllegalAccessErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (!hasMethodsCausingError()) { | 
|  | return false; | 
|  | } | 
|  | BooleanBox seenNoAccess = new BooleanBox(false); | 
|  | forEachFailureDependency( | 
|  | type -> | 
|  | appInfo | 
|  | .contextIndependentDefinitionForWithResolutionResult(type) | 
|  | .forEachClassResolutionResult( | 
|  | clazz -> | 
|  | seenNoAccess.or( | 
|  | AccessControl.isClassAccessible( | 
|  | clazz, | 
|  | context, | 
|  | appInfo.getClassToFeatureSplitMap(), | 
|  | appInfo.options(), | 
|  | appInfo.getStartupOrder(), | 
|  | appInfo.getSyntheticItems()) | 
|  | .isPossiblyFalse())), | 
|  | method -> { | 
|  | DexClass holder = appInfo.definitionFor(method.getHolderType()); | 
|  | DexClassAndMethod classAndMethod = DexClassAndMethod.create(holder, method); | 
|  | seenNoAccess.or( | 
|  | AccessControl.isMemberAccessible( | 
|  | classAndMethod, initialResolutionHolder, context, appInfo) | 
|  | .isPossiblyFalse()); | 
|  | }); | 
|  | return seenNoAccess.get(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isNoSuchMethodErrorResult(DexClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | if (!hasMethodsCausingError()) { | 
|  | return true; | 
|  | } | 
|  | if (isIllegalAccessErrorResult(context, appInfo)) { | 
|  | return false; | 
|  | } | 
|  | // At this point we know we have methods causing errors but we have access to them. To be | 
|  | // certain that this is the case where we have nest access but we are invoking a method with | 
|  | // an incorrect symbolic reference, we directly test for it by having an assert. | 
|  | assert verifyInvalidSymbolicReference(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyInvalidSymbolicReference() { | 
|  | BooleanBox invalidSymbolicReference = new BooleanBox(true); | 
|  | forEachFailureDependency( | 
|  | type -> { | 
|  | // Intentionally empty | 
|  | }, | 
|  | method -> { | 
|  | invalidSymbolicReference.and( | 
|  | method.getHolderType() != initialResolutionHolder.getType()); | 
|  | }); | 
|  | return invalidSymbolicReference.get(); | 
|  | } | 
|  | } | 
|  |  | 
|  | public abstract static class MultipleMethodResolutionResult< | 
|  | C extends DexClass & ProgramOrClasspathClass, T extends SingleResolutionResult<C>> | 
|  | extends MethodResolutionResult { | 
|  |  | 
|  | protected final T programOrClasspathResult; | 
|  | protected final List<SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | otherProgramOrClasspathResults; | 
|  | protected final List<SingleLibraryResolutionResult> libraryResolutionResults; | 
|  | protected final List<FailedResolutionResult> failedResolutionResults; | 
|  |  | 
|  | protected MultipleMethodResolutionResult( | 
|  | T programOrClasspathResult, | 
|  | List<SingleResolutionResult<? extends ProgramOrClasspathClass>> programOrClasspathResults, | 
|  | List<SingleLibraryResolutionResult> libraryResolutionResults, | 
|  | List<FailedResolutionResult> failedResolutionResults) { | 
|  | this.programOrClasspathResult = programOrClasspathResult; | 
|  | this.otherProgramOrClasspathResults = programOrClasspathResults; | 
|  | this.libraryResolutionResults = libraryResolutionResults; | 
|  | this.failedResolutionResults = failedResolutionResults; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public OptionalBool isAccessibleForVirtualDispatchFrom( | 
|  | ProgramDefinition context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isVirtualTarget() { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod lookupInvokeSpecialTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod lookupInvokeSuperTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeDirectTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedMethod lookupInvokeStaticTarget( | 
|  | DexProgramClass context, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | InstantiatedSubTypeInfo instantiatedInfo, | 
|  | PinnedPredicate pinnedPredicate) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupResult lookupVirtualDispatchTargets( | 
|  | DexProgramClass context, | 
|  | AppInfoWithLiveness appInfo, | 
|  | DexProgramClass refinedReceiverUpperBound, | 
|  | DexProgramClass refinedReceiverLowerBound) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | InstantiatedObject instance, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndMethod lookupVirtualDispatchTarget( | 
|  | DexClass dynamicInstance, AppInfoWithClassHierarchy appInfo) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public LookupTarget lookupVirtualDispatchTarget( | 
|  | LambdaDescriptor lambdaInstance, | 
|  | AppInfoWithClassHierarchy appInfo, | 
|  | Consumer<DexType> typeCausingFailureConsumer, | 
|  | Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { | 
|  | throw new Unreachable("Should not be called on MultipleFieldResolutionResult"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void visitMethodResolutionResults( | 
|  | Consumer<? super SingleResolutionResult<? extends ProgramOrClasspathClass>> | 
|  | programOrClasspathConsumer, | 
|  | Consumer<? super SingleLibraryResolutionResult> libraryResultConsumer, | 
|  | Consumer<? super ArrayCloneMethodResult> cloneResultConsumer, | 
|  | Consumer<? super FailedResolutionResult> failedResolutionConsumer) { | 
|  | if (programOrClasspathResult != null) { | 
|  | programOrClasspathConsumer.accept(programOrClasspathResult); | 
|  | } | 
|  | if (otherProgramOrClasspathResults != null) { | 
|  | otherProgramOrClasspathResults.forEach(programOrClasspathConsumer); | 
|  | } | 
|  | libraryResolutionResults.forEach(libraryResultConsumer); | 
|  | failedResolutionResults.forEach(failedResolutionConsumer); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isMultiMethodResolutionResult() { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class MultipleProgramWithLibraryResolutionResult | 
|  | extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> { | 
|  |  | 
|  | public MultipleProgramWithLibraryResolutionResult( | 
|  | SingleProgramResolutionResult programOrClasspathResult, | 
|  | List<SingleLibraryResolutionResult> libraryResolutionResults, | 
|  | List<FailedResolutionResult> failedOrUnknownResolutionResults) { | 
|  | super( | 
|  | programOrClasspathResult, | 
|  | null, | 
|  | libraryResolutionResults, | 
|  | failedOrUnknownResolutionResults); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class MultipleClasspathWithLibraryResolutionResult | 
|  | extends MultipleMethodResolutionResult<DexClasspathClass, SingleClasspathResolutionResult> { | 
|  |  | 
|  | public MultipleClasspathWithLibraryResolutionResult( | 
|  | SingleClasspathResolutionResult programOrClasspathResult, | 
|  | List<SingleLibraryResolutionResult> libraryResolutionResults, | 
|  | List<FailedResolutionResult> failedOrUnknownResolutionResults) { | 
|  | super( | 
|  | programOrClasspathResult, | 
|  | null, | 
|  | libraryResolutionResults, | 
|  | failedOrUnknownResolutionResults); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class MultipleLibraryMethodResolutionResult | 
|  | extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> { | 
|  |  | 
|  | public MultipleLibraryMethodResolutionResult( | 
|  | List<SingleLibraryResolutionResult> libraryResolutionResults, | 
|  | List<FailedResolutionResult> failedOrUnknownResolutionResults) { | 
|  | super(null, null, libraryResolutionResults, failedOrUnknownResolutionResults); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class MultipleMaximallySpecificResolutionResult | 
|  | extends MultipleMethodResolutionResult<DexProgramClass, SingleProgramResolutionResult> { | 
|  |  | 
|  | public MultipleMaximallySpecificResolutionResult( | 
|  | List<SingleResolutionResult<? extends ProgramOrClasspathClass>> programOrClasspathResult, | 
|  | List<SingleLibraryResolutionResult> libraryResolutionResults, | 
|  | List<FailedResolutionResult> failedResolutionResults) { | 
|  | super(null, programOrClasspathResult, libraryResolutionResults, failedResolutionResults); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static Builder builder() { | 
|  | return new Builder(); | 
|  | } | 
|  |  | 
|  | public static class Builder { | 
|  |  | 
|  | private MethodResolutionResult possiblySingleResult = null; | 
|  | private List<MethodResolutionResult> allResults = null; | 
|  | private boolean allowMultipleProgramResults = false; | 
|  |  | 
|  | private Builder() {} | 
|  |  | 
|  | public void addResolutionResult(MethodResolutionResult result) { | 
|  | if (possiblySingleResult == null) { | 
|  | possiblySingleResult = result; | 
|  | return; | 
|  | } | 
|  | if (allResults == null) { | 
|  | allResults = new ArrayList<>(); | 
|  | allResults.add(possiblySingleResult); | 
|  | } | 
|  | allResults.add(result); | 
|  | } | 
|  |  | 
|  | public Builder allowMultipleProgramResults() { | 
|  | allowMultipleProgramResults = true; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult buildOrIfEmpty( | 
|  | MethodResolutionResult emptyResult, DexType responsibleTypeForNoSuchMethodResult) { | 
|  | return buildOrIfEmpty( | 
|  | emptyResult, Collections.singletonList(responsibleTypeForNoSuchMethodResult)); | 
|  | } | 
|  |  | 
|  | public MethodResolutionResult buildOrIfEmpty( | 
|  | MethodResolutionResult emptyResult, | 
|  | Collection<DexType> responsibleTypesForNoSuchMethodResult) { | 
|  | if (possiblySingleResult == null) { | 
|  | return emptyResult; | 
|  | } else if (allResults == null) { | 
|  | return possiblySingleResult; | 
|  | } | 
|  | List<SingleResolutionResult<? extends ProgramOrClasspathClass>> programOrClasspathResults = | 
|  | new ArrayList<>(); | 
|  | List<SingleLibraryResolutionResult> libraryResults = new ArrayList<>(); | 
|  | List<FailedResolutionResult> failedResults = new ArrayList<>(); | 
|  | Set<NoSuchMethodResult> noSuchMethodResults = Sets.newIdentityHashSet(); | 
|  | allResults.forEach( | 
|  | otherResult -> { | 
|  | otherResult.visitMethodResolutionResults( | 
|  | otherProgramOrClasspathResult -> { | 
|  | if (!programOrClasspathResults.isEmpty() && !allowMultipleProgramResults) { | 
|  | assert false : "Unexpected multiple results between program and classpath"; | 
|  | } | 
|  | programOrClasspathResults.add(otherProgramOrClasspathResult); | 
|  | }, | 
|  | newLibraryResult -> { | 
|  | if (!Iterables.any( | 
|  | libraryResults, | 
|  | existing -> | 
|  | existing.getResolvedHolder() == newLibraryResult.getResolvedHolder())) { | 
|  | libraryResults.add(newLibraryResult); | 
|  | } | 
|  | }, | 
|  | ConsumerUtils.emptyConsumer(), | 
|  | newFailedResult -> { | 
|  | if (newFailedResult.internalIsInstanceOfNoSuchMethodResult()) { | 
|  | noSuchMethodResults.add(newFailedResult.asNoSuchMethodResult()); | 
|  | } | 
|  | if (!Iterables.any(failedResults, existing -> existing == newFailedResult)) { | 
|  | failedResults.add(newFailedResult); | 
|  | } | 
|  | }); | 
|  | }); | 
|  | // If we have seen a NoSuchMethod and also a successful result it must be because we have | 
|  | // multiple definitions of a type. Here we compute a single NoSuchMethodResult with root types | 
|  | // that must be preserved to still observe the NoSuchMethodError. | 
|  | if (!noSuchMethodResults.isEmpty()) { | 
|  | if (!libraryResults.isEmpty() || !programOrClasspathResults.isEmpty()) { | 
|  | failedResults.add( | 
|  | mergeNoSuchMethodErrors(noSuchMethodResults, responsibleTypesForNoSuchMethodResult)); | 
|  | } else { | 
|  | failedResults.add(NoSuchMethodResult.INSTANCE); | 
|  | } | 
|  | } | 
|  | if (programOrClasspathResults.isEmpty()) { | 
|  | if (libraryResults.size() == 1 && failedResults.isEmpty()) { | 
|  | return libraryResults.get(0); | 
|  | } else if (libraryResults.isEmpty() && failedResults.size() == 1) { | 
|  | return failedResults.get(0); | 
|  | } else { | 
|  | return new MultipleLibraryMethodResolutionResult(libraryResults, failedResults); | 
|  | } | 
|  | } else if (libraryResults.isEmpty() | 
|  | && failedResults.isEmpty() | 
|  | && programOrClasspathResults.size() == 1) { | 
|  | return programOrClasspathResults.get(0); | 
|  | } else if (programOrClasspathResults.size() == 1) { | 
|  | SingleResolutionResult<?> singleResult = programOrClasspathResults.get(0); | 
|  | if (singleResult.hasProgramResult()) { | 
|  | return new MultipleProgramWithLibraryResolutionResult( | 
|  | singleResult.asSingleProgramResolutionResult(), libraryResults, failedResults); | 
|  | } else { | 
|  | SingleClasspathResolutionResult classpathResult = | 
|  | singleResult.asSingleClasspathResolutionResult(); | 
|  | assert classpathResult != null; | 
|  | return new MultipleClasspathWithLibraryResolutionResult( | 
|  | classpathResult, libraryResults, failedResults); | 
|  | } | 
|  | } else { | 
|  | // This must be a maximally specific result since we have multiple program or classpath | 
|  | // values. | 
|  | return new MultipleMaximallySpecificResolutionResult( | 
|  | programOrClasspathResults, libraryResults, failedResults); | 
|  | } | 
|  | } | 
|  |  | 
|  | private NoSuchMethodResult mergeNoSuchMethodErrors( | 
|  | Set<NoSuchMethodResult> noSuchMethodErrors, Collection<DexType> typesCausingErrorsHere) { | 
|  | Set<DexType> typesCausingError = SetUtils.newIdentityHashSet(typesCausingErrorsHere); | 
|  | noSuchMethodErrors.forEach( | 
|  | failedResolutionResult -> { | 
|  | assert failedResolutionResult == NoSuchMethodResult.INSTANCE | 
|  | || failedResolutionResult.isNoSuchMethodResultDueToMultipleClassDefinitions(); | 
|  | if (failedResolutionResult.typesCausingError != null) { | 
|  | typesCausingError.addAll(failedResolutionResult.typesCausingError); | 
|  | } | 
|  | }); | 
|  | return typesCausingError.isEmpty() | 
|  | ? NoSuchMethodResult.INSTANCE | 
|  | : new NoSuchMethodResultDueToMultipleClassDefinitions(typesCausingError); | 
|  | } | 
|  | } | 
|  | } |