blob: 35b5484eba51a3576f9ebaaa3c5ed720596df67f [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 static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class FieldArrayBacking extends FieldCollectionBacking {
private DexEncodedField[] staticFields;
private DexEncodedField[] instanceFields;
public static FieldCollectionBacking fromArrays(
DexEncodedField[] staticFields, DexEncodedField[] instanceFields) {
return new FieldArrayBacking(staticFields, instanceFields);
}
private FieldArrayBacking(DexEncodedField[] staticFields, DexEncodedField[] instanceFields) {
assert staticFields != null;
assert instanceFields != null;
this.staticFields = staticFields;
this.instanceFields = instanceFields;
}
@Override
boolean verify() {
assert verifyNoDuplicateFields();
return true;
}
private boolean verifyNoDuplicateFields() {
Set<DexField> unique = Sets.newIdentityHashSet();
for (DexEncodedField field : fields(alwaysTrue())) {
boolean changed = unique.add(field.getReference());
assert changed : "Duplicate field `" + field.getReference().toSourceString() + "`";
}
return true;
}
@Override
int numberOfStaticFields() {
return staticFields.length;
}
@Override
int numberOfInstanceFields() {
return instanceFields.length;
}
@Override
int size() {
return staticFields.length + instanceFields.length;
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverse(
DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
TraversalContinuation<BT, CT> traversalContinuation = traverseStaticFields(holder, fn);
if (traversalContinuation.shouldBreak()) {
return traversalContinuation;
}
return traverseInstanceFields(holder, fn);
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverse(
DexClass holder,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
TraversalContinuation<BT, CT> traversalContinuation =
traverseStaticFields(holder, fn, initialValue);
if (traversalContinuation.shouldBreak()) {
return traversalContinuation;
}
return traverseInstanceFields(
holder, fn, traversalContinuation.asContinue().getValueOrDefault(null));
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
return traverseInstanceOrStaticFields(holder, instanceFields, fn);
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
DexClass holder,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
return traverseInstanceOrStaticFields(holder, instanceFields, fn, initialValue);
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
return traverseInstanceOrStaticFields(holder, staticFields, fn);
}
@Override
<BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
DexClass holder,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
return traverseInstanceOrStaticFields(holder, staticFields, fn, initialValue);
}
private static <BT, CT> TraversalContinuation<BT, CT> traverseInstanceOrStaticFields(
DexClass holder,
DexEncodedField[] fields,
Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
for (DexEncodedField definition : fields) {
DexClassAndField field = DexClassAndField.create(holder, definition);
traversalContinuation = fn.apply(field);
if (traversalContinuation.shouldBreak()) {
return traversalContinuation;
}
}
return traversalContinuation;
}
private static <BT, CT> TraversalContinuation<BT, CT> traverseInstanceOrStaticFields(
DexClass holder,
DexEncodedField[] fields,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
TraversalContinuation<BT, CT> traversalContinuation =
TraversalContinuation.doContinue(initialValue);
for (DexEncodedField definition : fields) {
DexClassAndField field = DexClassAndField.create(holder, definition);
traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
if (traversalContinuation.shouldBreak()) {
return traversalContinuation;
}
}
return traversalContinuation;
}
@Override
Iterable<DexEncodedField> fields(Predicate<? super DexEncodedField> predicate) {
return Iterables.concat(
Iterables.filter(Arrays.asList(instanceFields), predicate::test),
Iterables.filter(Arrays.asList(staticFields), predicate::test));
}
@Override
List<DexEncodedField> staticFieldsAsList() {
if (InternalOptions.assertionsEnabled()) {
return Collections.unmodifiableList(Arrays.asList(staticFields));
}
return Arrays.asList(staticFields);
}
@Override
void appendStaticField(DexEncodedField field) {
staticFields = appendFieldHelper(staticFields, field);
}
@Override
void appendStaticFields(Collection<DexEncodedField> fields) {
staticFields = appendFieldsHelper(staticFields, fields);
}
@Override
void clearStaticFields() {
staticFields = DexEncodedField.EMPTY_ARRAY;
}
@Override
public void setStaticFields(DexEncodedField[] fields) {
assert fields != null;
staticFields = fields;
}
@Override
List<DexEncodedField> instanceFieldsAsList() {
if (InternalOptions.assertionsEnabled()) {
return Collections.unmodifiableList(Arrays.asList(instanceFields));
}
return Arrays.asList(instanceFields);
}
@Override
void appendInstanceField(DexEncodedField field) {
instanceFields = appendFieldHelper(instanceFields, field);
}
@Override
void appendInstanceFields(Collection<DexEncodedField> fields) {
instanceFields = appendFieldsHelper(instanceFields, fields);
}
@Override
void clearInstanceFields() {
instanceFields = DexEncodedField.EMPTY_ARRAY;
}
@Override
void setInstanceFields(DexEncodedField[] fields) {
assert fields != null;
instanceFields = fields;
}
@Override
DexEncodedField lookupField(DexField field) {
DexEncodedField result = lookupInstanceField(field);
return result == null ? lookupStaticField(field) : result;
}
@Override
DexEncodedField lookupStaticField(DexField field) {
return lookupFieldHelper(staticFields, field);
}
@Override
DexEncodedField lookupInstanceField(DexField field) {
return lookupFieldHelper(instanceFields, field);
}
@Override
void replaceFields(Function<DexEncodedField, DexEncodedField> replacement) {
staticFields =
replaceFieldsHelper(
staticFields,
replacement,
FieldCollectionBacking::belongsInStaticPool,
this::appendInstanceFields);
instanceFields =
replaceFieldsHelper(
instanceFields,
replacement,
FieldCollectionBacking::belongsInInstancePool,
this::appendStaticFields);
}
private static DexEncodedField[] appendFieldHelper(
DexEncodedField[] existingItems, DexEncodedField itemToAppend) {
DexEncodedField[] newFields = new DexEncodedField[existingItems.length + 1];
System.arraycopy(existingItems, 0, newFields, 0, existingItems.length);
newFields[existingItems.length] = itemToAppend;
return newFields;
}
private static DexEncodedField[] appendFieldsHelper(
DexEncodedField[] existingItems, Collection<DexEncodedField> itemsToAppend) {
DexEncodedField[] newFields = new DexEncodedField[existingItems.length + itemsToAppend.size()];
System.arraycopy(existingItems, 0, newFields, 0, existingItems.length);
int i = existingItems.length;
for (DexEncodedField field : itemsToAppend) {
newFields[i] = field;
i++;
}
return newFields;
}
private static DexEncodedField lookupFieldHelper(DexEncodedField[] items, DexField reference) {
for (int i = 0; i < items.length; i++) {
DexEncodedField item = items[i];
if (reference.match(item)) {
return item;
}
}
return null;
}
@SuppressWarnings("ReferenceEquality")
private static DexEncodedField[] replaceFieldsHelper(
DexEncodedField[] fields,
Function<DexEncodedField, DexEncodedField> replacement,
Predicate<DexEncodedField> inThisPool,
Consumer<List<DexEncodedField>> onMovedToOtherPool) {
List<DexEncodedField> movedToOtherPool = new ArrayList<>();
for (int i = 0; i < fields.length; i++) {
DexEncodedField existingField = fields[i];
assert inThisPool.test(existingField);
DexEncodedField newField = replacement.apply(existingField);
assert newField != null;
if (existingField != newField) {
if (inThisPool.test(newField)) {
fields[i] = newField;
} else {
fields[i] = null;
movedToOtherPool.add(newField);
}
}
}
if (movedToOtherPool.isEmpty()) {
return fields;
}
onMovedToOtherPool.accept(movedToOtherPool);
return ArrayUtils.filter(
fields,
Objects::nonNull,
DexEncodedField.EMPTY_ARRAY,
fields.length - movedToOtherPool.size());
}
}