blob: 79b2bb2b5244913c8a4410cab1475497ad7f51d6 [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.policies;
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.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* Identifies when virtual method merging is required and bails out. This is needed to ensure that
* we don't need to synthesize any $r8$classId fields, such that the result of the final round of
* class merging can be described as a renaming only.
*/
public class NoVirtualMethodMerging extends MultiClassPolicy {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
public NoVirtualMethodMerging(AppView<? extends AppInfoWithClassHierarchy> appView, Mode mode) {
assert mode.isFinal();
this.appView = appView;
}
@Override
public Collection<MergeGroup> apply(MergeGroup group) {
Map<MergeGroup, Map<DexMethodSignature, ProgramMethod>> newGroups = new LinkedHashMap<>();
for (DexProgramClass clazz : group) {
Map<DexMethodSignature, ProgramMethod> classMethods = new HashMap<>();
clazz.forEachProgramVirtualMethodMatching(
DexEncodedMethod::isNonAbstractVirtualMethod,
method -> classMethods.put(method.getMethodSignature(), method));
MergeGroup newGroup = null;
for (Entry<MergeGroup, Map<DexMethodSignature, ProgramMethod>> entry : newGroups.entrySet()) {
MergeGroup candidateGroup = entry.getKey();
Map<DexMethodSignature, ProgramMethod> groupMethods = entry.getValue();
if (canAddNonAbstractVirtualMethodsToGroup(
clazz, classMethods.values(), candidateGroup, groupMethods)) {
newGroup = candidateGroup;
groupMethods.putAll(classMethods);
break;
}
}
if (newGroup != null) {
newGroup.add(clazz);
} else {
newGroups.put(new MergeGroup(clazz), classMethods);
}
}
return removeTrivialGroups(newGroups.keySet());
}
private boolean canAddNonAbstractVirtualMethodsToGroup(
DexProgramClass clazz,
Collection<ProgramMethod> methods,
MergeGroup group,
Map<DexMethodSignature, ProgramMethod> groupMethods) {
// For each of clazz' virtual methods, check that adding these methods to the group does not
// require method merging.
for (ProgramMethod method : methods) {
ProgramMethod groupMethod = groupMethods.get(method.getMethodSignature());
if (groupMethod != null || hasNonAbstractDefinitionInHierarchy(group, method)) {
return false;
}
}
// For each of the group's virtual methods, check that adding these methods clazz does not
// require method merging.
for (ProgramMethod method : groupMethods.values()) {
if (hasNonAbstractDefinitionInHierarchy(clazz, method)) {
return false;
}
}
return true;
}
private boolean hasNonAbstractDefinitionInHierarchy(MergeGroup group, ProgramMethod method) {
return hasNonAbstractDefinitionInSuperClass(group.getSuperType(), method)
|| hasNonAbstractDefinitionInSuperInterface(
SetUtils.newIdentityHashSet(IterableUtils.flatMap(group, DexClass::getInterfaces)),
method);
}
private boolean hasNonAbstractDefinitionInHierarchy(DexProgramClass clazz, ProgramMethod method) {
return hasNonAbstractDefinitionInSuperClass(clazz.getSuperType(), method)
|| hasNonAbstractDefinitionInSuperInterface(clazz.getInterfaces(), method);
}
private boolean hasNonAbstractDefinitionInSuperClass(DexType superType, ProgramMethod method) {
SingleResolutionResult resolutionResult =
appView
.appInfo()
.resolveMethodOnClass(method.getReference(), superType)
.asSingleResolution();
return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract();
}
private boolean hasNonAbstractDefinitionInSuperInterface(
Iterable<DexType> interfaceTypes, ProgramMethod method) {
return Iterables.any(
interfaceTypes,
interfaceType -> {
SingleResolutionResult resolutionResult =
appView
.appInfo()
.resolveMethodOnInterface(interfaceType, method.getReference())
.asSingleResolution();
return resolutionResult != null && !resolutionResult.getResolvedMethod().isAbstract();
});
}
@Override
public String getName() {
return "NoVirtualMethodMerging";
}
}