blob: 4b782b38489525b845eda934239638628e45db54 [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.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
TraversalContinuation<?, ?> traverse(Function<DexEncodedField, TraversalContinuation<?, ?>> fn) {
for (int i = 0; i < staticFields.length; i++) {
if (fn.apply(staticFields[i]).shouldBreak()) {
return TraversalContinuation.doBreak();
}
}
for (int i = 0; i < instanceFields.length; i++) {
if (fn.apply(instanceFields[i]).shouldBreak()) {
return TraversalContinuation.doBreak();
}
}
return TraversalContinuation.doContinue();
}
@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;
}
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());
}
}