| // 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; |
| } |
| } |
| } |