|  | // 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 com.android.tools.r8.graph.LookupResult.LookupResultSuccess.LookupResultCollectionState; | 
|  | import com.android.tools.r8.utils.WorkList; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.Set; | 
|  |  | 
|  | public class LookupCompletenessHelper { | 
|  |  | 
|  | private final PinnedPredicate pinnedPredicate; | 
|  |  | 
|  | private Set<DexType> pinnedInstantiations; | 
|  | private Set<DexMethod> pinnedMethods; | 
|  |  | 
|  | LookupCompletenessHelper(PinnedPredicate pinnedPredicate) { | 
|  | this.pinnedPredicate = pinnedPredicate; | 
|  | } | 
|  |  | 
|  | void checkClass(DexClass clazz) { | 
|  | if (pinnedPredicate.isPinned(clazz)) { | 
|  | if (pinnedInstantiations == null) { | 
|  | pinnedInstantiations = Sets.newIdentityHashSet(); | 
|  | } | 
|  | pinnedInstantiations.add(clazz.type); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkMethod(DexEncodedMethod method) { | 
|  | if (pinnedPredicate.isPinned(method)) { | 
|  | if (pinnedMethods == null) { | 
|  | pinnedMethods = Sets.newIdentityHashSet(); | 
|  | } | 
|  | pinnedMethods.add(method.getReference()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void checkDexClassAndMethod(LookupMethodTarget methodTarget) { | 
|  | checkClass(methodTarget.getHolder()); | 
|  | checkMethod(methodTarget.getDefinition()); | 
|  | } | 
|  |  | 
|  | LookupResultCollectionState computeCollectionState( | 
|  | DexMethod method, AppInfoWithClassHierarchy appInfo) { | 
|  | assert pinnedInstantiations == null || !pinnedInstantiations.isEmpty(); | 
|  | if (pinnedInstantiations == null) { | 
|  | return LookupResultCollectionState.Complete; | 
|  | } | 
|  | if (pinnedMethods != null) { | 
|  | return LookupResultCollectionState.Incomplete; | 
|  | } | 
|  | WorkList<DexType> workList = WorkList.newIdentityWorkList(pinnedInstantiations); | 
|  | while (workList.hasNext()) { | 
|  | if (isMethodKeptInSuperTypeOrIsLibrary(workList, method, appInfo)) { | 
|  | return LookupResultCollectionState.Incomplete; | 
|  | } | 
|  | } | 
|  | return LookupResultCollectionState.Complete; | 
|  | } | 
|  |  | 
|  | private boolean isMethodKeptInSuperTypeOrIsLibrary( | 
|  | WorkList<DexType> workList, DexMethod method, AppInfoWithClassHierarchy appInfo) { | 
|  | while (workList.hasNext()) { | 
|  | DexClass parent = appInfo.definitionFor(workList.next()); | 
|  | if (parent == null) { | 
|  | continue; | 
|  | } | 
|  | DexEncodedMethod methodInClass = parent.lookupVirtualMethod(method); | 
|  | if (methodInClass != null | 
|  | && (parent.isNotProgramClass() || pinnedPredicate.isPinned(methodInClass))) { | 
|  | return true; | 
|  | } | 
|  | if (parent.superType != null) { | 
|  | workList.addIfNotSeen(parent.superType); | 
|  | } | 
|  | workList.addIfNotSeen(parent.interfaces.values); | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } |