Introduce a field collection with array and map-based backings.
Bug: b/265148324
Change-Id: I5155084088ce04e389073314d763b4a952610add
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 85f928d..59bff83 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.TraversalContinuation;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
@@ -32,7 +31,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -59,10 +57,7 @@
private OptionalBool isResolvable = OptionalBool.unknown();
/** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
-
- /** Access has to be synchronized during concurrent collection/writing phase. */
- protected DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
+ protected final FieldCollection fieldCollection;
/** Access has to be synchronized during concurrent collection/writing phase. */
protected final MethodCollection methodCollection;
@@ -112,8 +107,7 @@
this.accessFlags = accessFlags;
this.superType = superType;
this.type = type;
- setStaticFields(staticFields);
- setInstanceFields(instanceFields);
+ this.fieldCollection = FieldCollection.create(this, staticFields, instanceFields);
this.methodCollection = methodCollectionFactory.create(this);
this.nestHost = nestHost;
this.nestMembers = nestMembers;
@@ -222,9 +216,7 @@
}
public Iterable<DexEncodedField> fields(final Predicate<? super DexEncodedField> predicate) {
- return Iterables.concat(
- Iterables.filter(Arrays.asList(instanceFields), predicate::test),
- Iterables.filter(Arrays.asList(staticFields), predicate::test));
+ return fieldCollection.fields(predicate);
}
public Iterable<DexEncodedMember<?, ?>> members() {
@@ -235,6 +227,10 @@
return Iterables.concat(fields(predicate), methods(predicate));
}
+ public FieldCollection getFieldCollection() {
+ return fieldCollection;
+ }
+
@Override
public MethodCollection getMethodCollection() {
return methodCollection;
@@ -326,6 +322,10 @@
methodCollection.forEachMethod(consumer);
}
+ public List<DexEncodedField> allFieldsSorted() {
+ return fieldCollection.allFieldsSorted();
+ }
+
public List<DexEncodedMethod> allMethodsSorted() {
return methodCollection.allMethodsSorted();
}
@@ -387,11 +387,7 @@
}
public List<DexEncodedField> staticFields() {
- assert staticFields != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(staticFields));
- }
- return Arrays.asList(staticFields);
+ return fieldCollection.staticFieldsAsList();
}
public Iterable<DexEncodedField> staticFields(Predicate<DexEncodedField> predicate) {
@@ -399,159 +395,71 @@
}
public void appendStaticField(DexEncodedField field) {
- DexEncodedField[] newFields = new DexEncodedField[staticFields.length + 1];
- System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
- newFields[staticFields.length] = field;
- staticFields = newFields;
- assert verifyCorrectnessOfFieldHolder(field);
- assert verifyNoDuplicateFields();
+ fieldCollection.appendStaticField(field);
}
public void appendStaticFields(Collection<DexEncodedField> fields) {
- DexEncodedField[] newFields = new DexEncodedField[staticFields.length + fields.size()];
- System.arraycopy(staticFields, 0, newFields, 0, staticFields.length);
- int i = staticFields.length;
- for (DexEncodedField field : fields) {
- newFields[i] = field;
- i++;
- }
- staticFields = newFields;
- assert verifyCorrectnessOfFieldHolders(fields);
- assert verifyNoDuplicateFields();
+ fieldCollection.appendStaticFields(fields);
}
public DexEncodedField[] clearStaticFields() {
- DexEncodedField[] previousFields = staticFields;
- setStaticFields(DexEncodedField.EMPTY_ARRAY);
- return previousFields;
- }
-
- public void removeStaticField(int index) {
- DexEncodedField[] newFields = new DexEncodedField[staticFields.length - 1];
- System.arraycopy(staticFields, 0, newFields, 0, index);
- System.arraycopy(staticFields, index + 1, newFields, index, staticFields.length - index - 1);
- staticFields = newFields;
- }
-
- public void setStaticField(int index, DexEncodedField field) {
- staticFields[index] = field;
- assert verifyCorrectnessOfFieldHolder(field);
- assert verifyNoDuplicateFields();
+ List<DexEncodedField> previousFields = staticFields();
+ fieldCollection.clearStaticFields();
+ return previousFields.toArray(DexEncodedField.EMPTY_ARRAY);
}
public void setStaticFields(DexEncodedField[] fields) {
- staticFields = MoreObjects.firstNonNull(fields, DexEncodedField.EMPTY_ARRAY);
- assert verifyCorrectnessOfFieldHolders(staticFields());
- assert verifyNoDuplicateFields();
+ fieldCollection.setStaticFields(fields);
}
public void setStaticFields(Collection<DexEncodedField> fields) {
setStaticFields(fields.toArray(DexEncodedField.EMPTY_ARRAY));
}
- public boolean definesStaticField(DexField field) {
- for (DexEncodedField encodedField : staticFields()) {
- if (encodedField.getReference() == field) {
- return true;
- }
- }
- return false;
- }
-
public List<DexEncodedField> instanceFields() {
- assert instanceFields != null;
- if (InternalOptions.assertionsEnabled()) {
- return Collections.unmodifiableList(Arrays.asList(instanceFields));
- }
- return Arrays.asList(instanceFields);
+ return fieldCollection.instanceFieldsAsList();
}
public Iterable<DexEncodedField> instanceFields(Predicate<? super DexEncodedField> predicate) {
- return Iterables.filter(Arrays.asList(instanceFields), predicate::test);
+ return Iterables.filter(instanceFields(), predicate::test);
}
public void appendInstanceField(DexEncodedField field) {
- DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + 1];
- System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
- newFields[instanceFields.length] = field;
- instanceFields = newFields;
- assert verifyCorrectnessOfFieldHolder(field);
- assert verifyNoDuplicateFields();
+ fieldCollection.appendInstanceField(field);
}
public void appendInstanceFields(Collection<DexEncodedField> fields) {
- DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + fields.size()];
- System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length);
- int i = instanceFields.length;
- for (DexEncodedField field : fields) {
- newFields[i] = field;
- i++;
- }
- instanceFields = newFields;
- assert verifyCorrectnessOfFieldHolders(fields);
- assert verifyNoDuplicateFields();
- }
-
- public void removeInstanceField(int index) {
- DexEncodedField[] newFields = new DexEncodedField[instanceFields.length - 1];
- System.arraycopy(instanceFields, 0, newFields, 0, index);
- System.arraycopy(
- instanceFields, index + 1, newFields, index, instanceFields.length - index - 1);
- instanceFields = newFields;
- }
-
- public void setInstanceField(int index, DexEncodedField field) {
- instanceFields[index] = field;
- assert verifyCorrectnessOfFieldHolder(field);
- assert verifyNoDuplicateFields();
+ fieldCollection.appendInstanceFields(fields);
}
public void setInstanceFields(DexEncodedField[] fields) {
- instanceFields = MoreObjects.firstNonNull(fields, DexEncodedField.EMPTY_ARRAY);
- assert verifyCorrectnessOfFieldHolders(instanceFields());
- assert verifyNoDuplicateFields();
+ fieldCollection.setInstanceFields(fields);
}
public DexEncodedField[] clearInstanceFields() {
- DexEncodedField[] previousFields = instanceFields;
- instanceFields = DexEncodedField.EMPTY_ARRAY;
- return previousFields;
+ List<DexEncodedField> previousFields = instanceFields();
+ fieldCollection.clearInstanceFields();
+ return previousFields.toArray(DexEncodedField.EMPTY_ARRAY);
}
- private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
- assert field.getHolderType() == type
- : "Expected field `"
- + field.getReference().toSourceString()
- + "` to have holder `"
- + type.toSourceString()
- + "`";
- return true;
+ /** Find method in this class matching {@param method}. */
+ public DexClassAndField lookupClassField(DexField field) {
+ return toClassFieldOrNull(lookupField(field));
}
- private boolean verifyCorrectnessOfFieldHolders(Iterable<DexEncodedField> fields) {
- for (DexEncodedField field : fields) {
- assert verifyCorrectnessOfFieldHolder(field);
- }
- return true;
- }
-
- private boolean verifyNoDuplicateFields() {
- Set<DexField> unique = Sets.newIdentityHashSet();
- for (DexEncodedField field : fields()) {
- boolean changed = unique.add(field.getReference());
- assert changed : "Duplicate field `" + field.getReference().toSourceString() + "`";
- }
- return true;
+ /** Find field in this class matching {@param field}. */
+ public DexEncodedField lookupField(DexField field) {
+ return fieldCollection.lookupField(field);
}
/** Find static field in this class matching {@param field}. */
public DexEncodedField lookupStaticField(DexField field) {
- return lookupTarget(staticFields, field);
+ return fieldCollection.lookupStaticField(field);
}
/** Find instance field in this class matching {@param field}. */
public DexEncodedField lookupInstanceField(DexField field) {
- return lookupTarget(instanceFields, field);
+ return fieldCollection.lookupInstanceField(field);
}
public DexEncodedField lookupUniqueInstanceFieldWithName(DexString name) {
@@ -576,21 +484,10 @@
return result;
}
- /** Find method in this class matching {@param method}. */
- public DexClassAndField lookupClassField(DexField field) {
- return toClassFieldOrNull(lookupField(field));
- }
-
private DexClassAndField toClassFieldOrNull(DexEncodedField field) {
return field != null ? DexClassAndField.create(this, field) : null;
}
- /** Find field in this class matching {@param field}. */
- public DexEncodedField lookupField(DexField field) {
- DexEncodedField result = lookupInstanceField(field);
- return result == null ? lookupStaticField(field) : result;
- }
-
/** Find direct method in this class matching {@param method}. */
public DexEncodedMethod lookupDirectMethod(DexMethod method) {
return methodCollection.getDirectMethod(method);
@@ -676,16 +573,6 @@
&& method.getReference().proto.parameters.values[0] == factory.objectArrayType;
}
- private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> D lookupTarget(
- D[] items, R descriptor) {
- for (D entry : items) {
- if (descriptor.match(entry)) {
- return entry;
- }
- }
- return null;
- }
-
public boolean canBeInstantiatedByNewInstance() {
return !isAbstract() && !isAnnotation() && !isInterface();
}
@@ -1238,22 +1125,11 @@
}
public boolean hasStaticFields() {
- return staticFields.length > 0;
+ return fieldCollection.hasStaticFields();
}
public boolean hasInstanceFields() {
- return instanceFields.length > 0;
- }
-
- public boolean hasInstanceFieldsDirectlyOrIndirectly(AppView<?> appView) {
- if (superType == null || type == appView.dexItemFactory().objectType) {
- return false;
- }
- if (hasInstanceFields()) {
- return true;
- }
- DexClass superClass = appView.definitionFor(superType);
- return superClass == null || superClass.hasInstanceFieldsDirectlyOrIndirectly(appView);
+ return fieldCollection.hasInstanceFields();
}
public List<DexEncodedField> getDirectAndIndirectInstanceFields(AppView<?> appView) {
@@ -1269,8 +1145,7 @@
public boolean isValid(InternalOptions options) {
assert verifyNoAbstractMethodsOnNonAbstractClasses(virtualMethods(), options);
assert !isInterface() || !getMethodCollection().hasVirtualMethods(DexEncodedMethod::isFinal);
- assert verifyCorrectnessOfFieldHolders(fields());
- assert verifyNoDuplicateFields();
+ assert fieldCollection.verify();
assert methodCollection.verify();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
index 43c2532..aca2a14 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClasspathClass.java
@@ -161,8 +161,7 @@
.withItem(DexDefinition::annotations)
// TODO(b/158159959): Make signatures structural.
.withAssert(c -> c.classSignature == ClassSignature.noSignature())
- .withItemArray(c -> c.staticFields)
- .withItemArray(c -> c.instanceFields)
+ .withItemCollection(DexClass::allFieldsSorted)
.withItemCollection(DexClass::allMethodsSorted);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexFieldSignature.java b/src/main/java/com/android/tools/r8/graph/DexFieldSignature.java
new file mode 100644
index 0000000..2f22c1a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/DexFieldSignature.java
@@ -0,0 +1,66 @@
+// 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.structural.Equatable;
+import com.android.tools.r8.utils.structural.StructuralItem;
+import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.util.Objects;
+
+public class DexFieldSignature implements StructuralItem<DexFieldSignature> {
+
+ private final DexString name;
+ private final DexType type;
+
+ private static void specify(StructuralSpecification<DexFieldSignature, ?> spec) {
+ spec.withItem(DexFieldSignature::getName).withItem(DexFieldSignature::getType);
+ }
+
+ public static DexFieldSignature fromField(DexField field) {
+ return new DexFieldSignature(field.getName(), field.getType());
+ }
+
+ private DexFieldSignature(DexString name, DexType type) {
+ this.name = name;
+ this.type = type;
+ }
+
+ public DexString getName() {
+ return name;
+ }
+
+ public DexType getType() {
+ return type;
+ }
+
+ public boolean match(DexField field) {
+ return getName().equals(field.getName()) && getType().equals(field.getType());
+ }
+
+ @Override
+ public DexFieldSignature self() {
+ return this;
+ }
+
+ @Override
+ public StructuralMapping<DexFieldSignature> getStructuralMapping() {
+ return DexFieldSignature::specify;
+ }
+
+ @Override
+ public boolean isEqualTo(DexFieldSignature other) {
+ return getName() == other.getName() && getType() == other.getType();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return Equatable.equalsImpl(this, o);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 34d5646..0cb6881 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -244,8 +244,7 @@
.withItem(DexDefinition::annotations)
// TODO(b/158159959): Make signatures structural.
.withAssert(c -> c.classSignature == ClassSignature.noSignature())
- .withItemArray(c -> c.staticFields)
- .withItemArray(c -> c.instanceFields)
+ .withItemCollection(DexClass::allFieldsSorted)
.withItemCollection(DexClass::allMethodsSorted);
}
@@ -475,8 +474,7 @@
if (hasMethodsOrFields()) {
collector.add(this);
methodCollection.forEachMethod(m -> m.collectMixedSectionItems(collector));
- collectAll(collector, staticFields);
- collectAll(collector, instanceFields);
+ fieldCollection.forEachField(f -> f.collectMixedSectionItems(collector));
}
annotations().collectMixedSectionItems(collector);
if (interfaces != null) {
@@ -643,7 +641,7 @@
}
public boolean hasFields() {
- return instanceFields.length + staticFields.length > 0;
+ return fieldCollection.size() > 0;
}
public boolean hasMethods() {
@@ -658,19 +656,16 @@
public boolean hasClassOrMemberAnnotations() {
return !annotations().isEmpty()
|| hasAnnotations(methodCollection)
- || hasAnnotations(staticFields)
- || hasAnnotations(instanceFields);
+ || hasAnnotations(fieldCollection);
}
boolean hasOnlyInternalizableAnnotations() {
- return !hasAnnotations(methodCollection)
- && !hasAnnotations(staticFields)
- && !hasAnnotations(instanceFields);
+ return !hasAnnotations(methodCollection) && !hasAnnotations(fieldCollection);
}
- private boolean hasAnnotations(DexEncodedField[] fields) {
+ private boolean hasAnnotations(FieldCollection fields) {
synchronized (fields) {
- return Arrays.stream(fields).anyMatch(DexEncodedField::hasAnnotations);
+ return fields.hasAnnotations();
}
}
@@ -685,13 +680,12 @@
if (!hasNonDefaultStaticFieldValues()) {
return null;
}
- DexEncodedField[] fields = staticFields;
- Arrays.sort(
- fields, (a, b) -> a.getReference().compareToWithNamingLens(b.getReference(), namingLens));
+ List<DexEncodedField> fields = new ArrayList<>(staticFields());
+ fields.sort((a, b) -> a.getReference().compareToWithNamingLens(b.getReference(), namingLens));
int length = 0;
- List<DexValue> values = new ArrayList<>(fields.length);
- for (int i = 0; i < fields.length; i++) {
- DexEncodedField field = fields[i];
+ List<DexValue> values = new ArrayList<>(fields.size());
+ for (int i = 0; i < fields.size(); i++) {
+ DexEncodedField field = fields.get(i);
DexValue staticValue = field.getStaticValue();
assert staticValue != null;
values.add(staticValue);
@@ -705,7 +699,7 @@
}
private boolean hasNonDefaultStaticFieldValues() {
- for (DexEncodedField field : staticFields) {
+ for (DexEncodedField field : staticFields()) {
DexValue value = field.getStaticValue();
if (value != null && !value.isDefault(field.getReference().type)) {
return true;
diff --git a/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
new file mode 100644
index 0000000..4b782b3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
@@ -0,0 +1,243 @@
+// 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());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollection.java b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
new file mode 100644
index 0000000..e2cf122
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
@@ -0,0 +1,180 @@
+// 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.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<DexEncodedField> fn) {
+ backing.traverse(
+ field -> {
+ fn.accept(field);
+ return TraversalContinuation.doContinue();
+ });
+ }
+
+ public Iterable<DexEncodedField> fields(Predicate<? super DexEncodedField> predicate) {
+ return backing.fields(predicate);
+ }
+
+ public boolean verify() {
+ forEachField(
+ field -> {
+ assert verifyCorrectnessOfFieldHolder(field);
+ });
+ assert backing.verify();
+ return true;
+ }
+
+ 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(sorted::add);
+ sorted.sort(Comparator.comparing(DexEncodedMember::getReference));
+ return sorted;
+ }
+
+ public boolean hasAnnotations() {
+ return backing
+ .traverse(
+ field ->
+ field.hasAnnotations()
+ ? TraversalContinuation.doBreak()
+ : TraversalContinuation.doContinue())
+ .shouldBreak();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
new file mode 100644
index 0000000..570996f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
@@ -0,0 +1,72 @@
+// 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.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public abstract class FieldCollectionBacking {
+
+ // Internal consistency.
+
+ static boolean belongsInStaticPool(DexEncodedField field) {
+ return field.isStatic();
+ }
+
+ static boolean belongsInInstancePool(DexEncodedField field) {
+ return !belongsInStaticPool(field);
+ }
+
+ abstract boolean verify();
+
+ // Traversal methods.
+
+ abstract TraversalContinuation<?, ?> traverse(
+ Function<DexEncodedField, TraversalContinuation<?, ?>> fn);
+
+ // Collection methods.
+
+ abstract int size();
+
+ abstract Iterable<DexEncodedField> fields(Predicate<? super DexEncodedField> predicate);
+
+ // Specialized to static fields.
+
+ abstract int numberOfStaticFields();
+
+ abstract List<DexEncodedField> staticFieldsAsList();
+
+ abstract void appendStaticField(DexEncodedField field);
+
+ abstract void appendStaticFields(Collection<DexEncodedField> fields);
+
+ abstract void clearStaticFields();
+
+ abstract void setStaticFields(DexEncodedField[] fields);
+
+ // Specialized to instance fields.
+
+ abstract int numberOfInstanceFields();
+
+ abstract List<DexEncodedField> instanceFieldsAsList();
+
+ abstract void appendInstanceField(DexEncodedField field);
+
+ abstract void appendInstanceFields(Collection<DexEncodedField> fields);
+
+ abstract void clearInstanceFields();
+
+ abstract void setInstanceFields(DexEncodedField[] fields);
+
+ abstract DexEncodedField lookupField(DexField field);
+
+ abstract DexEncodedField lookupStaticField(DexField field);
+
+ abstract DexEncodedField lookupInstanceField(DexField field);
+
+ abstract void replaceFields(Function<DexEncodedField, DexEncodedField> replacement);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
new file mode 100644
index 0000000..3bac4e0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
@@ -0,0 +1,219 @@
+// 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.IterableUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
+import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public class FieldMapBacking extends FieldCollectionBacking {
+
+ private SortedMap<DexFieldSignature, DexEncodedField> fieldMap;
+
+ public static FieldMapBacking createLinked(int capacity) {
+ return new FieldMapBacking(createdLinkedMap(capacity));
+ }
+
+ private static SortedMap<DexFieldSignature, DexEncodedField> createdLinkedMap(int capacity) {
+ return new Object2ReferenceLinkedOpenHashMap<>(capacity);
+ }
+
+ private FieldMapBacking(SortedMap<DexFieldSignature, DexEncodedField> fieldMap) {
+ this.fieldMap = fieldMap;
+ }
+
+ // Internal map allocation that shall preserve the map-backing type.
+ // Only the linked map exists for fields currently.
+ private SortedMap<DexFieldSignature, DexEncodedField> internalCreateMap(int capacity) {
+ return createdLinkedMap(capacity);
+ }
+
+ @Override
+ boolean verify() {
+ fieldMap.forEach(
+ (signature, field) -> {
+ assert signature.match(field.getReference());
+ });
+ return true;
+ }
+
+ @Override
+ TraversalContinuation<?, ?> traverse(Function<DexEncodedField, TraversalContinuation<?, ?>> fn) {
+ for (DexEncodedField field : fieldMap.values()) {
+ TraversalContinuation<?, ?> result = fn.apply(field);
+ if (result.shouldBreak()) {
+ return result;
+ }
+ }
+ return TraversalContinuation.doContinue();
+ }
+
+ @Override
+ int size() {
+ return fieldMap.size();
+ }
+
+ @Override
+ Iterable<DexEncodedField> fields(Predicate<? super DexEncodedField> predicate) {
+ return IterableUtils.filter(fieldMap.values(), predicate);
+ }
+
+ @Override
+ int numberOfStaticFields() {
+ return numberOfFieldsHelper(FieldCollectionBacking::belongsInStaticPool);
+ }
+
+ @Override
+ List<DexEncodedField> staticFieldsAsList() {
+ return fieldsAsListHelper(FieldCollectionBacking::belongsInStaticPool);
+ }
+
+ @Override
+ void appendStaticField(DexEncodedField field) {
+ assert belongsInStaticPool(field);
+ DexEncodedField old = fieldMap.put(getSignature(field), field);
+ assert old == null;
+ }
+
+ @Override
+ void appendStaticFields(Collection<DexEncodedField> fields) {
+ fields.forEach(this::appendStaticField);
+ }
+
+ @Override
+ void clearStaticFields() {
+ fieldMap.values().removeIf(FieldCollectionBacking::belongsInStaticPool);
+ }
+
+ @Override
+ void setStaticFields(DexEncodedField[] fields) {
+ setFieldsInPoolHelper(fields, FieldCollectionBacking::belongsInStaticPool);
+ }
+
+ @Override
+ int numberOfInstanceFields() {
+ return numberOfFieldsHelper(FieldCollectionBacking::belongsInInstancePool);
+ }
+
+ @Override
+ List<DexEncodedField> instanceFieldsAsList() {
+ return fieldsAsListHelper(FieldCollectionBacking::belongsInInstancePool);
+ }
+
+ @Override
+ void appendInstanceField(DexEncodedField field) {
+ assert belongsInInstancePool(field);
+ DexEncodedField old = fieldMap.put(getSignature(field), field);
+ assert old == null;
+ }
+
+ @Override
+ void appendInstanceFields(Collection<DexEncodedField> fields) {
+ fields.forEach(this::appendInstanceField);
+ }
+
+ @Override
+ void clearInstanceFields() {
+ fieldMap.values().removeIf(FieldCollectionBacking::belongsInInstancePool);
+ }
+
+ @Override
+ void setInstanceFields(DexEncodedField[] fields) {
+ setFieldsInPoolHelper(fields, FieldCollectionBacking::belongsInInstancePool);
+ }
+
+ @Override
+ DexEncodedField lookupField(DexField field) {
+ return fieldMap.get(getSignature(field));
+ }
+
+ @Override
+ DexEncodedField lookupStaticField(DexField field) {
+ DexEncodedField result = lookupField(field);
+ return result != null && belongsInStaticPool(result) ? result : null;
+ }
+
+ @Override
+ DexEncodedField lookupInstanceField(DexField field) {
+ DexEncodedField result = lookupField(field);
+ return result != null && belongsInInstancePool(result) ? result : null;
+ }
+
+ @Override
+ void replaceFields(Function<DexEncodedField, DexEncodedField> replacement) {
+ // The code assumes that when replacement.apply(field) is called, the map is up-to-date with
+ // the previously replaced fields. We therefore cannot postpone the map updates to the end of
+ // the replacement.
+ ArrayList<DexEncodedField> initialValues = new ArrayList<>(fieldMap.values());
+ for (DexEncodedField field : initialValues) {
+ DexEncodedField newField = replacement.apply(field);
+ if (newField != field) {
+ DexFieldSignature oldSignature = getSignature(field);
+ DexFieldSignature newSignature = getSignature(newField);
+ if (!newSignature.isEqualTo(oldSignature)) {
+ if (fieldMap.get(oldSignature) == field) {
+ fieldMap.remove(oldSignature);
+ }
+ }
+ fieldMap.put(newSignature, newField);
+ }
+ }
+ }
+
+ private DexFieldSignature getSignature(DexEncodedField field) {
+ return getSignature(field.getReference());
+ }
+
+ private DexFieldSignature getSignature(DexField field) {
+ return DexFieldSignature.fromField(field);
+ }
+
+ private int numberOfFieldsHelper(Predicate<DexEncodedField> predicate) {
+ int count = 0;
+ for (DexEncodedField field : fieldMap.values()) {
+ if (predicate.test(field)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private List<DexEncodedField> fieldsAsListHelper(Predicate<DexEncodedField> predicate) {
+ List<DexEncodedField> result = new ArrayList<>(fieldMap.size());
+ fieldMap.forEach(
+ (signature, field) -> {
+ if (predicate.test(field)) {
+ result.add(field);
+ }
+ });
+ return Collections.unmodifiableList(result);
+ }
+
+ private void setFieldsInPoolHelper(
+ DexEncodedField[] fields, Predicate<DexEncodedField> inThisPool) {
+ if (fields.length == 0 && fieldMap.isEmpty()) {
+ return;
+ }
+ SortedMap<DexFieldSignature, DexEncodedField> newMap =
+ internalCreateMap(size() + fields.length);
+ fieldMap.forEach(
+ (signature, field) -> {
+ if (!inThisPool.test(field)) {
+ newMap.put(signature, field);
+ }
+ });
+ for (DexEncodedField field : fields) {
+ assert inThisPool.test(field);
+ newMap.put(getSignature(field), field);
+ }
+ fieldMap = newMap;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 44eed32..77100b5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -126,8 +126,7 @@
localUtilityClass.getDefinition().setStaticFields(localUtilityFields);
} else {
clazz.getMethodCollection().replaceMethods(this::fixupEncodedMethod);
- fixupFields(clazz.staticFields(), clazz::setStaticField);
- fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+ clazz.getFieldCollection().replaceFields(this::fixupEncodedField);
}
}
@@ -613,32 +612,27 @@
return newMethod;
}
- private void fixupFields(List<DexEncodedField> fields, DexClass.FieldSetter setter) {
- if (fields == null) {
- return;
+ private DexEncodedField fixupEncodedField(DexEncodedField encodedField) {
+ DexField field = encodedField.getReference();
+ DexType newType = fixupType(field.type);
+ if (newType == field.type) {
+ return encodedField;
}
- for (int i = 0; i < fields.size(); i++) {
- DexEncodedField encodedField = fields.get(i);
- DexField field = encodedField.getReference();
- DexType newType = fixupType(field.type);
- if (newType != field.type) {
- DexField newField = field.withType(newType, factory);
- lensBuilder.move(field, newField);
- DexEncodedField newEncodedField =
- encodedField.toTypeSubstitutedField(
- appView,
- newField,
- builder ->
- builder.setAbstractValue(
- encodedField.getOptimizationInfo().getAbstractValue(), appView));
- setter.setField(i, newEncodedField);
- if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
- assert encodedField.getStaticValue() == DexValue.DexValueNull.NULL;
- newEncodedField.setStaticValue(DexValue.DexValueInt.DEFAULT);
- // TODO(b/150593449): Support conversion from DexValueEnum to DexValueInt.
- }
- }
+ DexField newField = field.withType(newType, factory);
+ lensBuilder.move(field, newField);
+ DexEncodedField newEncodedField =
+ encodedField.toTypeSubstitutedField(
+ appView,
+ newField,
+ builder ->
+ builder.setAbstractValue(
+ encodedField.getOptimizationInfo().getAbstractValue(), appView));
+ if (encodedField.isStatic() && encodedField.hasExplicitStaticValue()) {
+ assert encodedField.getStaticValue() == DexValue.DexValueNull.NULL;
+ newEncodedField.setStaticValue(DexValue.DexValueInt.DEFAULT);
+ // TODO(b/150593449): Support conversion from DexValueEnum to DexValueInt.
}
+ return newEncodedField;
}
private DexProto fixupProto(DexProto proto) {