blob: 458f0baa53dc83a59a6baea6743a4c932b201c4f [file] [log] [blame]
/*
* // Copyright (c) 2020, 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.horizontalclassmerging;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class MergeGroup implements Collection<DexProgramClass> {
public static class Metadata {}
private final LinkedList<DexProgramClass> classes;
private DexField classIdField;
private DexProgramClass target = null;
private Metadata metadata = null;
private BidirectionalManyToOneMap<DexEncodedField, DexEncodedField> instanceFieldMap;
public MergeGroup() {
this.classes = new LinkedList<>();
}
public MergeGroup(DexProgramClass clazz) {
this();
add(clazz);
}
public MergeGroup(Iterable<DexProgramClass> classes) {
this();
Iterables.addAll(this.classes, classes);
}
public void applyMetadataFrom(MergeGroup group) {
if (metadata == null) {
metadata = group.metadata;
}
}
@Override
public boolean add(DexProgramClass clazz) {
return classes.add(clazz);
}
public boolean add(MergeGroup group) {
return classes.addAll(group.getClasses());
}
@Override
public boolean addAll(Collection<? extends DexProgramClass> classes) {
return this.classes.addAll(classes);
}
@Override
public void clear() {
classes.clear();
}
@Override
public boolean contains(Object o) {
return classes.contains(o);
}
@Override
public boolean containsAll(Collection<?> collection) {
return classes.containsAll(collection);
}
public void forEachSource(Consumer<DexProgramClass> consumer) {
assert hasTarget();
for (DexProgramClass clazz : classes) {
if (clazz != target) {
consumer.accept(clazz);
}
}
}
public LinkedList<DexProgramClass> getClasses() {
return classes;
}
public boolean hasClassIdField() {
return classIdField != null;
}
public DexField getClassIdField() {
assert hasClassIdField();
return classIdField;
}
public void setClassIdField(DexField classIdField) {
this.classIdField = classIdField;
}
public boolean hasInstanceFieldMap() {
return instanceFieldMap != null;
}
public BidirectionalManyToOneMap<DexEncodedField, DexEncodedField> getInstanceFieldMap() {
assert hasInstanceFieldMap();
return instanceFieldMap;
}
public void selectInstanceFieldMap(AppView<? extends AppInfoWithClassHierarchy> appView) {
assert hasTarget();
MutableBidirectionalManyToOneMap<DexEncodedField, DexEncodedField> instanceFieldMap =
BidirectionalManyToOneHashMap.newLinkedHashMap();
forEachSource(
source ->
ClassInstanceFieldsMerger.mapFields(appView, source, target, instanceFieldMap::put));
setInstanceFieldMap(instanceFieldMap);
}
public void setInstanceFieldMap(
BidirectionalManyToOneMap<DexEncodedField, DexEncodedField> instanceFieldMap) {
assert !hasInstanceFieldMap();
this.instanceFieldMap = instanceFieldMap;
}
public Iterable<DexProgramClass> getSources() {
assert hasTarget();
return Iterables.filter(classes, clazz -> clazz != target);
}
public DexType getSuperType() {
assert IterableUtils.allIdentical(classes, DexClass::getSuperType);
return getClasses().getFirst().getSuperType();
}
public boolean hasTarget() {
return target != null;
}
public DexProgramClass getTarget() {
return target;
}
public ProgramField getTargetInstanceField(ProgramField field) {
assert hasTarget();
assert hasInstanceFieldMap();
if (field.getHolder() == getTarget()) {
return field;
}
DexEncodedField targetField = getInstanceFieldMap().get(field.getDefinition());
return new ProgramField(getTarget(), targetField);
}
public void selectTarget(AppView<? extends AppInfoWithClassHierarchy> appView) {
Iterable<DexProgramClass> candidates = Iterables.filter(getClasses(), DexClass::isPublic);
if (IterableUtils.isEmpty(candidates)) {
candidates = getClasses();
}
Iterator<DexProgramClass> candidateIterator = candidates.iterator();
DexProgramClass target = IterableUtils.first(candidates);
while (candidateIterator.hasNext()) {
DexProgramClass current = candidateIterator.next();
KeepClassInfo keepClassInfo = appView.getKeepInfo().getClassInfo(current);
if (keepClassInfo.isMinificationAllowed(appView.options())) {
target = current;
break;
}
// Select the target with the shortest name.
if (current.getType().getDescriptor().size() < target.getType().getDescriptor().size) {
target = current;
}
}
setTarget(appView.testing().horizontalClassMergingTarget.apply(appView, candidates, target));
}
private void setTarget(DexProgramClass target) {
assert !hasTarget();
this.target = target;
}
public boolean isTrivial() {
return size() < 2;
}
public boolean isNonTrivial() {
return !isTrivial();
}
@Override
public boolean isEmpty() {
return classes.isEmpty();
}
public boolean isClassGroup() {
return !isInterfaceGroup();
}
public boolean isInterfaceGroup() {
assert !isEmpty();
assert IterableUtils.allIdentical(getClasses(), DexClass::isInterface);
return getClasses().getFirst().isInterface();
}
@Override
public Iterator<DexProgramClass> iterator() {
return classes.iterator();
}
@Override
public int size() {
return classes.size();
}
@Override
public boolean remove(Object o) {
return classes.remove(o);
}
@Override
public boolean removeAll(Collection<?> collection) {
return classes.removeAll(collection);
}
public DexProgramClass removeFirst(Predicate<DexProgramClass> predicate) {
return IteratorUtils.removeFirst(iterator(), predicate);
}
@Override
public boolean removeIf(Predicate<? super DexProgramClass> predicate) {
return classes.removeIf(predicate);
}
public DexProgramClass removeLast() {
return classes.removeLast();
}
@Override
public boolean retainAll(Collection<?> collection) {
return collection.retainAll(collection);
}
@Override
public Object[] toArray() {
return classes.toArray();
}
@Override
public <T> T[] toArray(T[] ts) {
return classes.toArray(ts);
}
}