blob: 029b8e48380f8a62dc7a82067fb56d23ed0742f6 [file] [log] [blame]
// 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.errors.Unimplemented;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public abstract class FieldResolutionResult
extends MemberResolutionResult<DexEncodedField, DexField> {
public static FailedFieldResolutionResult failure() {
return FailedFieldResolutionResult.INSTANCE;
}
public static UnknownFieldResolutionResult unknown() {
return UnknownFieldResolutionResult.INSTANCE;
}
@Override
public boolean isFieldResolutionResult() {
return true;
}
@Override
public FieldResolutionResult asFieldResolutionResult() {
return this;
}
public DexEncodedField getResolvedField() {
return null;
}
public DexField getResolvedFieldReference() {
return null;
}
@Override
public DexClassAndField getResolutionPair() {
return null;
}
public ProgramField getSingleProgramField() {
return null;
}
public ProgramField getProgramField() {
return null;
}
public boolean isSingleFieldResolutionResult() {
return false;
}
public boolean isSingleProgramFieldResolutionResult() {
return false;
}
public boolean hasSuccessfulResolutionResult() {
return false;
}
public SingleFieldResolutionResult<?> asSingleFieldResolutionResult() {
return null;
}
public SingleProgramFieldResolutionResult asSingleProgramFieldResolutionResult() {
return null;
}
public SingleClasspathFieldResolutionResult asSingleClasspathFieldResolutionResult() {
return null;
}
@Override
public boolean isSuccessfulMemberResolutionResult() {
return false;
}
@Override
public SingleFieldResolutionResult<?> asSuccessfulMemberResolutionResult() {
return null;
}
public boolean isPossiblyFailedOrUnknownResolution() {
return false;
}
public boolean hasProgramOrClasspathResult() {
return false;
}
public boolean hasProgramResult() {
return false;
}
public boolean hasClasspathResult() {
return false;
}
public boolean isMultiFieldResolutionResult() {
return false;
}
public final void forEachFieldResolutionResult(Consumer<FieldResolutionResult> resultConsumer) {
visitFieldResolutionResults(resultConsumer, resultConsumer, resultConsumer);
}
public final void forEachSuccessfulFieldResolutionResult(
Consumer<SingleFieldResolutionResult<?>> resultConsumer) {
visitFieldResolutionResults(resultConsumer, failedResult -> {});
}
public final void visitFieldResolutionResults(
Consumer<SingleFieldResolutionResult<?>> singleResultConsumer,
Consumer<FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
visitFieldResolutionResults(
singleResultConsumer, singleResultConsumer, failedResolutionConsumer);
}
public abstract void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer);
public DexClass getInitialResolutionHolder() {
return null;
}
public static SingleFieldResolutionResult<?> createSingleFieldResolutionResult(
DexClass initialResolutionHolder, DexClass holder, DexEncodedField definition) {
if (holder.isLibraryClass()) {
return new SingleLibraryFieldResolutionResult(
initialResolutionHolder, holder.asLibraryClass(), definition);
} else if (holder.isClasspathClass()) {
return new SingleClasspathFieldResolutionResult(
initialResolutionHolder, holder.asClasspathClass(), definition);
} else {
assert holder.isProgramClass();
return new SingleProgramFieldResolutionResult(
initialResolutionHolder, holder.asProgramClass(), definition);
}
}
public abstract static class SingleFieldResolutionResult<T extends DexClass>
extends FieldResolutionResult
implements SuccessfulMemberResolutionResult<DexEncodedField, DexField> {
private final DexClass initialResolutionHolder;
private final T resolvedHolder;
private final DexEncodedField resolvedField;
SingleFieldResolutionResult(
DexClass initialResolutionHolder, T resolvedHolder, DexEncodedField resolvedField) {
assert resolvedHolder.type == resolvedField.getHolderType();
this.initialResolutionHolder = initialResolutionHolder;
this.resolvedHolder = resolvedHolder;
this.resolvedField = resolvedField;
}
@Override
public DexClass getInitialResolutionHolder() {
return initialResolutionHolder;
}
@Override
public T getResolvedHolder() {
return resolvedHolder;
}
@Override
public DexEncodedField getResolvedField() {
return resolvedField;
}
@Override
public DexField getResolvedFieldReference() {
return resolvedField.getReference();
}
@Override
public DexEncodedField getResolvedMember() {
return resolvedField;
}
@Override
public DexClassAndField getResolutionPair() {
return DexClassAndField.create(resolvedHolder, resolvedField);
}
@Override
public OptionalBool isAccessibleFrom(
ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
return AccessControl.isMemberAccessible(this, context, appInfo);
}
@Override
public boolean isSingleFieldResolutionResult() {
return true;
}
@Override
public SingleFieldResolutionResult<T> asSingleFieldResolutionResult() {
return this;
}
@Override
public boolean isSuccessfulMemberResolutionResult() {
return true;
}
@Override
public SingleFieldResolutionResult<T> asSuccessfulMemberResolutionResult() {
return this;
}
@Override
public boolean hasSuccessfulResolutionResult() {
return true;
}
}
public static class SingleProgramFieldResolutionResult
extends SingleFieldResolutionResult<DexProgramClass> {
SingleProgramFieldResolutionResult(
DexClass initialResolutionHolder,
DexProgramClass resolvedHolder,
DexEncodedField resolvedField) {
super(initialResolutionHolder, resolvedHolder, resolvedField);
}
@Override
public ProgramField getProgramField() {
return getSingleProgramField();
}
@Override
public ProgramField getSingleProgramField() {
return new ProgramField(getResolvedHolder(), getResolvedField());
}
@Override
public boolean isSingleProgramFieldResolutionResult() {
return true;
}
@Override
public SingleProgramFieldResolutionResult asSingleProgramFieldResolutionResult() {
return this;
}
@Override
public boolean hasProgramOrClasspathResult() {
return true;
}
@Override
public boolean hasProgramResult() {
return true;
}
@Override
public void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
programOrClasspathConsumer.accept(this);
}
}
public static class SingleClasspathFieldResolutionResult
extends SingleFieldResolutionResult<DexClasspathClass> {
SingleClasspathFieldResolutionResult(
DexClass initialResolutionHolder,
DexClasspathClass resolvedHolder,
DexEncodedField resolvedField) {
super(initialResolutionHolder, resolvedHolder, resolvedField);
}
@Override
public SingleClasspathFieldResolutionResult asSingleClasspathFieldResolutionResult() {
return this;
}
@Override
public boolean hasProgramOrClasspathResult() {
return true;
}
@Override
public boolean hasClasspathResult() {
return true;
}
@Override
public void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
programOrClasspathConsumer.accept(this);
}
}
public static class SingleLibraryFieldResolutionResult
extends SingleFieldResolutionResult<DexLibraryClass> {
SingleLibraryFieldResolutionResult(
DexClass initialResolutionHolder,
DexLibraryClass resolvedHolder,
DexEncodedField resolvedField) {
super(initialResolutionHolder, resolvedHolder, resolvedField);
}
@Override
public void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
libraryResultConsumer.accept(this);
}
}
public abstract static class MultipleFieldResolutionResult<
C extends DexClass & ProgramOrClasspathClass, T extends SingleFieldResolutionResult<C>>
extends FieldResolutionResult {
protected final T programOrClasspathResult;
protected final List<SingleLibraryFieldResolutionResult> libraryResolutionResults;
protected final List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults;
public MultipleFieldResolutionResult(
T programOrClasspathResult,
List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
assert programOrClasspathResult == null
|| !programOrClasspathResult.getResolvedHolder().isLibraryClass();
assert failedOrUnknownResolutionResults.stream()
.allMatch(FieldResolutionResult::isPossiblyFailedOrUnknownResolution);
assert BooleanUtils.intValue(programOrClasspathResult != null)
+ libraryResolutionResults.size()
+ failedOrUnknownResolutionResults.size()
> 1
: "Should have been a single or failed result";
this.programOrClasspathResult = programOrClasspathResult;
this.libraryResolutionResults = libraryResolutionResults;
this.failedOrUnknownResolutionResults = failedOrUnknownResolutionResults;
}
@Override
public boolean isMultiFieldResolutionResult() {
return true;
}
@Override
public DexClass getInitialResolutionHolder() {
throw new Unimplemented("Should not be called on MultipleFieldResolutionResult");
}
@Override
public boolean hasProgramOrClasspathResult() {
return programOrClasspathResult != null;
}
@Override
public boolean hasProgramResult() {
return programOrClasspathResult != null && programOrClasspathResult.hasProgramResult();
}
@Override
public boolean hasClasspathResult() {
return programOrClasspathResult != null && programOrClasspathResult.hasClasspathResult();
}
@Override
public OptionalBool isAccessibleFrom(
ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
throw new Unimplemented("Should not be called on MultipleFieldResolutionResult");
}
@Override
public boolean isPossiblyFailedOrUnknownResolution() {
return !failedOrUnknownResolutionResults.isEmpty();
}
@Override
public boolean isSuccessfulMemberResolutionResult() {
return failedOrUnknownResolutionResults.isEmpty();
}
@Override
public void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
if (programOrClasspathResult != null) {
programOrClasspathConsumer.accept(programOrClasspathResult);
}
libraryResolutionResults.forEach(libraryResultConsumer);
failedOrUnknownResolutionResults.forEach(failedResolutionConsumer);
}
@Override
public boolean hasSuccessfulResolutionResult() {
return hasProgramOrClasspathResult() || !libraryResolutionResults.isEmpty();
}
}
public static class MultipleProgramWithLibraryFieldResolutionResult
extends MultipleFieldResolutionResult<DexProgramClass, SingleProgramFieldResolutionResult> {
public MultipleProgramWithLibraryFieldResolutionResult(
SingleProgramFieldResolutionResult programOrClasspathResult,
List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
}
@Override
public ProgramField getProgramField() {
return programOrClasspathResult == null ? null : programOrClasspathResult.getProgramField();
}
}
public static class MultipleClasspathWithLibraryFieldResolutionResult
extends MultipleFieldResolutionResult<
DexClasspathClass, SingleClasspathFieldResolutionResult> {
public MultipleClasspathWithLibraryFieldResolutionResult(
SingleClasspathFieldResolutionResult programOrClasspathResult,
List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
super(programOrClasspathResult, libraryResolutionResults, failedOrUnknownResolutionResults);
}
}
public static class MultipleLibraryFieldResolutionResult
extends MultipleFieldResolutionResult<DexProgramClass, SingleProgramFieldResolutionResult> {
public MultipleLibraryFieldResolutionResult(
List<SingleLibraryFieldResolutionResult> libraryResolutionResults,
List<FailedOrUnknownFieldResolutionResult> failedOrUnknownResolutionResults) {
super(null, libraryResolutionResults, failedOrUnknownResolutionResults);
}
}
public abstract static class FailedOrUnknownFieldResolutionResult extends FieldResolutionResult {
@Override
public OptionalBool isAccessibleFrom(
ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
return OptionalBool.FALSE;
}
@Override
public void visitFieldResolutionResults(
Consumer<? super SingleFieldResolutionResult<?>> programOrClasspathConsumer,
Consumer<? super SingleLibraryFieldResolutionResult> libraryResultConsumer,
Consumer<? super FailedOrUnknownFieldResolutionResult> failedResolutionConsumer) {
failedResolutionConsumer.accept(this);
}
@Override
public boolean isPossiblyFailedOrUnknownResolution() {
return true;
}
}
public static class FailedFieldResolutionResult extends FailedOrUnknownFieldResolutionResult {
private static final FailedFieldResolutionResult INSTANCE = new FailedFieldResolutionResult();
@Override
public boolean isFailedResolution() {
return true;
}
}
/**
* Used in D8 when trying to resolve a field that is not declared on the enclosing class of the
* current method.
*/
public static class UnknownFieldResolutionResult extends FailedOrUnknownFieldResolutionResult {
private static final UnknownFieldResolutionResult INSTANCE = new UnknownFieldResolutionResult();
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private FieldResolutionResult currentResult = null;
private Builder() {}
public void addResolutionResult(FieldResolutionResult otherResult) {
if (currentResult == null) {
currentResult = otherResult;
return;
}
Box<SingleFieldResolutionResult<?>> singleResult = new Box<>();
List<SingleLibraryFieldResolutionResult> libraryResults = new ArrayList<>();
List<FailedOrUnknownFieldResolutionResult> failedResults = new ArrayList<>();
currentResult.visitFieldResolutionResults(
singleResult::set, libraryResults::add, failedResults::add);
otherResult.visitFieldResolutionResults(
otherProgramOrClasspathResult -> {
if (singleResult.isSet()) {
assert false : "Unexpected multiple results between program and classpath";
if (singleResult.get().hasProgramResult()) {
return;
}
}
singleResult.set(otherProgramOrClasspathResult);
},
newLibraryResult -> {
if (!Iterables.any(
libraryResults,
existing -> existing.getResolvedHolder() == newLibraryResult.getResolvedHolder())) {
libraryResults.add(newLibraryResult);
}
},
newFailedResult -> {
if (!Iterables.any(
failedResults,
existing ->
existing.isFailedResolution() == newFailedResult.isFailedResolution())) {
failedResults.add(newFailedResult);
}
});
if (!singleResult.isSet()) {
if (libraryResults.size() == 1 && failedResults.isEmpty()) {
currentResult = libraryResults.get(0);
} else if (libraryResults.isEmpty() && failedResults.size() == 1) {
currentResult = failedResults.get(0);
} else {
currentResult = new MultipleLibraryFieldResolutionResult(libraryResults, failedResults);
}
} else if (libraryResults.isEmpty() && failedResults.isEmpty()) {
currentResult = singleResult.get();
} else if (singleResult.get().hasProgramResult()) {
currentResult =
new MultipleProgramWithLibraryFieldResolutionResult(
singleResult.get().asSingleProgramFieldResolutionResult(),
libraryResults,
failedResults);
} else {
SingleClasspathFieldResolutionResult classpathResult =
singleResult.get().asSingleClasspathFieldResolutionResult();
assert classpathResult != null;
currentResult =
new MultipleClasspathWithLibraryFieldResolutionResult(
classpathResult, libraryResults, failedResults);
}
}
public FieldResolutionResult buildOrIfEmpty(FieldResolutionResult emptyResult) {
return currentResult == null ? emptyResult : currentResult;
}
}
}