blob: 2c4b920a865cb15496a935d7c4e2ee2cc53c942d [file] [log] [blame]
// 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();
}
}