|  | // Copyright (c) 2023, 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.utils.TraversalContinuation; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Collection; | 
|  | import java.util.Comparator; | 
|  | import java.util.List; | 
|  | import java.util.function.BiFunction; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.function.Function; | 
|  | import java.util.function.Predicate; | 
|  |  | 
|  | public class FieldCollection { | 
|  |  | 
|  | // Threshold between using an array and a map for the backing store. | 
|  | // The choice of 30 is just a copy from the method backing threshold. | 
|  | private static final int ARRAY_BACKING_THRESHOLD = 30; | 
|  |  | 
|  | private final DexClass holder; | 
|  | private FieldCollectionBacking backing; | 
|  |  | 
|  | private FieldCollection(DexClass holder, FieldCollectionBacking backing) { | 
|  | this.holder = holder; | 
|  | this.backing = backing; | 
|  | } | 
|  |  | 
|  | public static FieldCollection create( | 
|  | DexClass holder, DexEncodedField[] staticFields, DexEncodedField[] instanceFields) { | 
|  | int fieldCount = staticFields.length + instanceFields.length; | 
|  | FieldCollectionBacking backing; | 
|  | if (fieldCount > ARRAY_BACKING_THRESHOLD) { | 
|  | backing = FieldMapBacking.createLinked(fieldCount); | 
|  | backing.setStaticFields(staticFields); | 
|  | backing.setInstanceFields(instanceFields); | 
|  | } else { | 
|  | backing = FieldArrayBacking.fromArrays(staticFields, instanceFields); | 
|  | } | 
|  | return createInternal(holder, backing); | 
|  | } | 
|  |  | 
|  | private static FieldCollection createInternal(DexClass holder, FieldCollectionBacking backing) { | 
|  | // Internal create mirrors MethodCollection in case of adding a concurrency checker. | 
|  | return new FieldCollection(holder, backing); | 
|  | } | 
|  |  | 
|  | public int size() { | 
|  | return backing.size(); | 
|  | } | 
|  |  | 
|  | public void forEachField(Consumer<DexClassAndField> fn) { | 
|  | traverse( | 
|  | field -> { | 
|  | fn.accept(field); | 
|  | return TraversalContinuation.doContinue(); | 
|  | }); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedField> fields(Predicate<? super DexEncodedField> predicate) { | 
|  | return backing.fields(predicate); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverse( | 
|  | Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | 
|  | return backing.traverse(holder, fn); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverse( | 
|  | BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | 
|  | CT initialValue) { | 
|  | return backing.traverse(holder, fn, initialValue); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields( | 
|  | Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | 
|  | return backing.traverseInstanceFields(holder, fn); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields( | 
|  | BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | 
|  | CT initialValue) { | 
|  | return backing.traverseInstanceFields(holder, fn, initialValue); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields( | 
|  | Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | 
|  | return backing.traverseStaticFields(holder, fn); | 
|  | } | 
|  |  | 
|  | public <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields( | 
|  | BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | 
|  | CT initialValue) { | 
|  | return backing.traverseStaticFields(holder, fn, initialValue); | 
|  | } | 
|  |  | 
|  | public boolean verify() { | 
|  | forEachField( | 
|  | field -> { | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | }); | 
|  | assert backing.verify(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyCorrectnessOfFieldHolder(DexClassAndField field) { | 
|  | assert verifyCorrectnessOfFieldHolder(field.getDefinition()); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @SuppressWarnings("ReferenceEquality") | 
|  | private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) { | 
|  | assert field.getHolderType() == holder.type | 
|  | : "Expected field `" | 
|  | + field.getReference().toSourceString() | 
|  | + "` to have holder `" | 
|  | + holder.type.toSourceString() | 
|  | + "`"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyCorrectnessOfFieldHolders(Iterable<DexEncodedField> fields) { | 
|  | for (DexEncodedField field : fields) { | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public boolean hasStaticFields() { | 
|  | return backing.numberOfStaticFields() > 0; | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> staticFieldsAsList() { | 
|  | return backing.staticFieldsAsList(); | 
|  | } | 
|  |  | 
|  | public void appendStaticField(DexEncodedField field) { | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | backing.appendStaticField(field); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public void appendStaticFields(Collection<DexEncodedField> fields) { | 
|  | assert verifyCorrectnessOfFieldHolders(fields); | 
|  | backing.appendStaticFields(fields); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public void clearStaticFields() { | 
|  | backing.clearStaticFields(); | 
|  | } | 
|  |  | 
|  | public void setStaticFields(DexEncodedField[] fields) { | 
|  | backing.setStaticFields(fields); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public boolean hasInstanceFields() { | 
|  | return backing.numberOfInstanceFields() > 0; | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> instanceFieldsAsList() { | 
|  | return backing.instanceFieldsAsList(); | 
|  | } | 
|  |  | 
|  | public void appendInstanceField(DexEncodedField field) { | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | backing.appendInstanceField(field); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public void appendInstanceFields(Collection<DexEncodedField> fields) { | 
|  | assert verifyCorrectnessOfFieldHolders(fields); | 
|  | backing.appendInstanceFields(fields); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public void clearInstanceFields() { | 
|  | backing.clearInstanceFields(); | 
|  | } | 
|  |  | 
|  | public void setInstanceFields(DexEncodedField[] fields) { | 
|  | backing.setInstanceFields(fields); | 
|  | assert backing.verify(); | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupField(DexField field) { | 
|  | return backing.lookupField(field); | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupStaticField(DexField field) { | 
|  | return backing.lookupStaticField(field); | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupInstanceField(DexField field) { | 
|  | return backing.lookupInstanceField(field); | 
|  | } | 
|  |  | 
|  | public void replaceFields(Function<DexEncodedField, DexEncodedField> replacement) { | 
|  | backing.replaceFields(replacement); | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> allFieldsSorted() { | 
|  | List<DexEncodedField> sorted = new ArrayList<>(size()); | 
|  | forEachField(field -> sorted.add(field.getDefinition())); | 
|  | sorted.sort(Comparator.comparing(DexEncodedMember::getReference)); | 
|  | return sorted; | 
|  | } | 
|  |  | 
|  | public boolean hasAnnotations() { | 
|  | return traverse(field -> TraversalContinuation.breakIf(field.getDefinition().hasAnnotations())) | 
|  | .shouldBreak(); | 
|  | } | 
|  | } |