| // 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.google.common.collect.Sets; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| |
| public abstract class LookupResult { |
| |
| public boolean isLookupResultFailure() { |
| return false; |
| } |
| |
| public boolean isLookupResultSuccess() { |
| return false; |
| } |
| |
| public LookupResultFailure asLookupResultFailure() { |
| return null; |
| } |
| |
| public LookupResultSuccess asLookupResultSuccess() { |
| return null; |
| } |
| |
| public final void forEach(Consumer<? super LookupTarget> onTarget) { |
| forEach(onTarget, onTarget); |
| } |
| |
| public abstract void forEach( |
| Consumer<? super LookupMethodTarget> onMethodTarget, |
| Consumer<? super LookupLambdaTarget> onLambdaTarget); |
| |
| public abstract void forEachFailureDependency( |
| Consumer<? super DexEncodedMethod> methodCausingFailureConsumer); |
| |
| public static LookupResultSuccess createResult( |
| Map<DexMethod, LookupMethodTarget> methodTargets, |
| List<LookupLambdaTarget> lambdaTargets, |
| List<DexEncodedMethod> methodsCausingFailure, |
| LookupResultCollectionState state) { |
| return new LookupResultSuccess(methodTargets, lambdaTargets, methodsCausingFailure, state); |
| } |
| |
| public static LookupResultFailure createFailedResult() { |
| return LookupResultFailure.INSTANCE; |
| } |
| |
| public static LookupResultSuccess getIncompleteEmptyResult() { |
| return LookupResultSuccess.EMPTY_INSTANCE; |
| } |
| |
| public static class LookupResultSuccess extends LookupResult { |
| |
| private static final LookupResultSuccess EMPTY_INSTANCE = |
| new LookupResultSuccess( |
| new IdentityHashMap<>(), |
| Collections.emptyList(), |
| Collections.emptyList(), |
| LookupResultCollectionState.Incomplete); |
| |
| private final Map<DexMethod, LookupMethodTarget> methodTargets; |
| private final List<LookupLambdaTarget> lambdaTargets; |
| private final List<DexEncodedMethod> methodsCausingFailure; |
| private LookupResultCollectionState state; |
| |
| private LookupResultSuccess( |
| Map<DexMethod, LookupMethodTarget> methodTargets, |
| List<LookupLambdaTarget> lambdaTargets, |
| List<DexEncodedMethod> methodsCausingFailure, |
| LookupResultCollectionState state) { |
| this.methodTargets = methodTargets; |
| this.lambdaTargets = lambdaTargets; |
| this.methodsCausingFailure = methodsCausingFailure; |
| this.state = state; |
| } |
| |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| public boolean isEmpty() { |
| return methodTargets.isEmpty() && lambdaTargets.isEmpty(); |
| } |
| |
| public boolean hasMethodTargets() { |
| return !methodTargets.isEmpty(); |
| } |
| |
| public boolean hasLambdaTargets() { |
| return !lambdaTargets.isEmpty(); |
| } |
| |
| @Override |
| public void forEach( |
| Consumer<? super LookupMethodTarget> onMethodTarget, |
| Consumer<? super LookupLambdaTarget> onLambdaTarget) { |
| methodTargets.forEach((key, value) -> onMethodTarget.accept(value)); |
| lambdaTargets.forEach(onLambdaTarget); |
| } |
| |
| @Override |
| public void forEachFailureDependency( |
| Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { |
| methodsCausingFailure.forEach(methodCausingFailureConsumer); |
| } |
| |
| public boolean contains(DexEncodedMethod method) { |
| // Containment of a method in the lookup results only pertains to the method targets. |
| return methodTargets.containsKey(method.getReference()); |
| } |
| |
| @Override |
| public LookupResultSuccess asLookupResultSuccess() { |
| return this; |
| } |
| |
| @Override |
| public boolean isLookupResultSuccess() { |
| return true; |
| } |
| |
| public boolean isIncomplete() { |
| return state == LookupResultCollectionState.Incomplete; |
| } |
| |
| public boolean isComplete() { |
| return state == LookupResultCollectionState.Complete; |
| } |
| |
| public void setIncomplete() { |
| // TODO(b/148769279): Remove when we have instantiated info. |
| state = LookupResultCollectionState.Incomplete; |
| } |
| |
| public LookupTarget getSingleLookupTarget() { |
| if (isIncomplete() || methodTargets.size() + lambdaTargets.size() > 1) { |
| return null; |
| } |
| // TODO(b/150932978): Check lambda targets implementation methods. |
| if (methodTargets.size() == 1) { |
| return methodTargets.values().iterator().next(); |
| } else if (lambdaTargets.size() == 1) { |
| return lambdaTargets.get(0); |
| } |
| return null; |
| } |
| |
| public enum LookupResultCollectionState { |
| Complete, |
| Incomplete, |
| } |
| |
| public static class Builder { |
| |
| private final Map<DexMethod, LookupMethodTarget> methodTargets = new IdentityHashMap<>(); |
| private final List<LookupLambdaTarget> lambdaTargets = new ArrayList<>(); |
| private final List<DexEncodedMethod> methodsCausingFailure = new ArrayList<>(); |
| private final Set<DexType> typesCausingFailure = Sets.newIdentityHashSet(); |
| private LookupResultCollectionState state; |
| |
| public Builder addMethodTarget(LookupMethodTarget methodTarget) { |
| assert methodTarget.isMethodTarget(); |
| methodTargets.putIfAbsent(methodTarget.asMethodTarget().getReference(), methodTarget); |
| return this; |
| } |
| |
| public Builder addLambdaTarget(LookupLambdaTarget lambdaTarget) { |
| lambdaTargets.add(lambdaTarget); |
| return this; |
| } |
| |
| public Builder addMethodCausingFailure(DexEncodedMethod methodCausingFailure) { |
| methodsCausingFailure.add(methodCausingFailure); |
| return this; |
| } |
| |
| public Builder addTypeCausingFailure(DexType typeCausingFailure) { |
| typesCausingFailure.add(typeCausingFailure); |
| return this; |
| } |
| |
| public Builder setState(LookupResultCollectionState state) { |
| this.state = state; |
| return this; |
| } |
| |
| public LookupResultSuccess build() { |
| return new LookupResultSuccess(methodTargets, lambdaTargets, methodsCausingFailure, state); |
| } |
| } |
| } |
| |
| public static class LookupResultFailure extends LookupResult { |
| |
| private static final LookupResultFailure INSTANCE = new LookupResultFailure(); |
| |
| private LookupResultFailure() { |
| // Empty to only allow creation locally. |
| } |
| |
| @Override |
| public LookupResultFailure asLookupResultFailure() { |
| return this; |
| } |
| |
| @Override |
| public boolean isLookupResultFailure() { |
| return true; |
| } |
| |
| @Override |
| public void forEach( |
| Consumer<? super LookupMethodTarget> onMethodTarget, |
| Consumer<? super LookupLambdaTarget> onLambdaTarget) { |
| // Nothing to iterate for a failed lookup. |
| } |
| |
| @Override |
| public void forEachFailureDependency( |
| Consumer<? super DexEncodedMethod> methodCausingFailureConsumer) { |
| // TODO: record and emit failure dependencies. |
| } |
| } |
| } |