| // 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.internal.IterableUtils; | |
| import com.android.tools.r8.utils.internal.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.BiFunction; | |
| import java.util.function.Consumer; | |
| 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 | |
| void forEach(Consumer<DexEncodedField> fn) { | |
| fieldMap.values().forEach(fn); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverse( | |
| DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | |
| return traverseFieldsThatMatches(holder, fn, alwaysTrue()); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverse( | |
| DexClass holder, | |
| BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | |
| CT initialValue) { | |
| return traverseFieldsThatMatches(holder, fn, alwaysTrue(), initialValue); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields( | |
| DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | |
| return traverseFieldsThatMatches(holder, fn, DexEncodedField::isInstance); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields( | |
| DexClass holder, | |
| BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | |
| CT initialValue) { | |
| return traverseFieldsThatMatches(holder, fn, DexEncodedField::isInstance, initialValue); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields( | |
| DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) { | |
| return traverseFieldsThatMatches(holder, fn, DexEncodedField::isStatic); | |
| } | |
| @Override | |
| <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields( | |
| DexClass holder, | |
| BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | |
| CT initialValue) { | |
| return traverseFieldsThatMatches(holder, fn, DexEncodedField::isStatic, initialValue); | |
| } | |
| private <BT, CT> TraversalContinuation<BT, CT> traverseFieldsThatMatches( | |
| DexClass holder, | |
| Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn, | |
| Predicate<? super DexEncodedField> predicate) { | |
| TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue(); | |
| for (DexEncodedField definition : fieldMap.values()) { | |
| if (predicate.test(definition)) { | |
| DexClassAndField field = DexClassAndField.create(holder, definition); | |
| traversalContinuation = fn.apply(field); | |
| if (traversalContinuation.shouldBreak()) { | |
| return traversalContinuation; | |
| } | |
| } | |
| } | |
| return traversalContinuation; | |
| } | |
| private <BT, CT> TraversalContinuation<BT, CT> traverseFieldsThatMatches( | |
| DexClass holder, | |
| BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn, | |
| Predicate<? super DexEncodedField> predicate, | |
| CT initialValue) { | |
| TraversalContinuation<BT, CT> traversalContinuation = | |
| TraversalContinuation.doContinue(initialValue); | |
| for (DexEncodedField definition : fieldMap.values()) { | |
| if (predicate.test(definition)) { | |
| DexClassAndField field = DexClassAndField.create(holder, definition); | |
| traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue()); | |
| if (traversalContinuation.shouldBreak()) { | |
| return traversalContinuation; | |
| } | |
| } | |
| } | |
| return traversalContinuation; | |
| } | |
| @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 | |
| @SuppressWarnings("ReferenceEquality") | |
| 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; | |
| } | |
| } |