blob: d9f6def904942f00bcc9c73775cbf8f10b49b835 [file] [log] [blame]
// Copyright (c) 2022, 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.dex;
import com.android.tools.r8.dex.FileWriter.MixedSectionOffsets;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexWritableCode;
import com.android.tools.r8.graph.DexWritableCode.DexWritableCacheKey;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.utils.collections.ProgramMethodMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DefaultMixedSectionLayoutStrategy extends MixedSectionLayoutStrategy {
final AppView<?> appView;
final MixedSectionOffsets mixedSectionOffsets;
public DefaultMixedSectionLayoutStrategy(
AppView<?> appView, MixedSectionOffsets mixedSectionOffsets) {
this.appView = appView;
this.mixedSectionOffsets = mixedSectionOffsets;
}
@Override
public Collection<DexAnnotation> getAnnotationLayout() {
return mixedSectionOffsets.getAnnotations();
}
@Override
public Collection<DexAnnotationDirectory> getAnnotationDirectoryLayout() {
return mixedSectionOffsets.getAnnotationDirectories();
}
@Override
public Collection<DexAnnotationSet> getAnnotationSetLayout() {
return mixedSectionOffsets.getAnnotationSets();
}
@Override
public Collection<ParameterAnnotationsList> getAnnotationSetRefListLayout() {
return mixedSectionOffsets.getAnnotationSetRefLists();
}
@Override
public Collection<DexProgramClass> getClassDataLayout() {
return mixedSectionOffsets.getClassesWithData();
}
@Override
public Collection<ProgramMethod> getCodeLayout() {
return getCodeLayoutForClasses(mixedSectionOffsets.getClassesWithData());
}
private static class DeduplicatedCodeCounts {
private Map<DexWritableCacheKey, Integer> counts;
private final AppView<?> appView;
private DeduplicatedCodeCounts(AppView<?> appView) {
this.appView = appView;
}
public void addCode(DexWritableCode dexWritableCode, ProgramMethod method) {
assert appView.options().canUseCanonicalizedCodeObjects();
if (counts == null) {
counts = new HashMap<>();
}
DexWritableCacheKey cacheKey =
dexWritableCode.getCacheLookupKey(method, appView.dexItemFactory());
if (!counts.containsKey(cacheKey)) {
counts.put(cacheKey, 1);
} else {
counts.put(cacheKey, counts.get(cacheKey) + 1);
}
}
public int getCount(ProgramMethod method) {
if (counts == null) {
assert !appView.options().canUseCanonicalizedCodeObjects()
|| method.getDefinition().getDexWritableCodeOrNull() == null;
return 1;
}
DexWritableCode dexWritableCodeOrNull = method.getDefinition().getDexWritableCodeOrNull();
DexWritableCacheKey cacheLookupKey =
dexWritableCodeOrNull.getCacheLookupKey(method, appView.dexItemFactory());
assert counts.containsKey(cacheLookupKey);
return counts.get(cacheLookupKey);
}
}
final Collection<ProgramMethod> getCodeLayoutForClasses(Collection<DexProgramClass> classes) {
ProgramMethodMap<String> codeToDexSortingKeyMap = ProgramMethodMap.create();
List<ProgramMethod> codesSorted = new ArrayList<>();
DeduplicatedCodeCounts codeCounts = new DeduplicatedCodeCounts(appView);
for (DexProgramClass clazz : classes) {
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
DexWritableCode code = method.getDefinition().getDexWritableCodeOrNull();
assert code != null || method.getDefinition().shouldNotHaveCode();
if (code != null) {
if (appView.options().canUseCanonicalizedCodeObjects()) {
codeCounts.addCode(code, method);
}
codesSorted.add(method);
codeToDexSortingKeyMap.put(
method, getKeyForDexCodeSorting(method, appView.app().getProguardMap()));
}
});
}
Comparator<ProgramMethod> defaultCodeSorting =
Comparator.comparing(codeToDexSortingKeyMap::get);
if (appView.options().canUseCanonicalizedCodeObjects()) {
codesSorted.sort(
Comparator.comparingInt(codeCounts::getCount).thenComparing(defaultCodeSorting));
} else {
codesSorted.sort(defaultCodeSorting);
}
return codesSorted;
}
private static String getKeyForDexCodeSorting(ProgramMethod method, ClassNameMapper proguardMap) {
// TODO(b/173999869): Could this instead compute sorting using dex items?
Signature signature;
String originalClassName;
if (proguardMap != null) {
signature = proguardMap.originalSignatureOf(method.getReference());
originalClassName = proguardMap.originalNameOf(method.getHolderType());
} else {
signature = MethodSignature.fromDexMethod(method.getReference());
originalClassName = method.getHolderType().toSourceString();
}
return originalClassName + signature;
}
@Override
public Collection<DexEncodedArray> getEncodedArrayLayout() {
return mixedSectionOffsets.getEncodedArrays();
}
@Override
public Collection<DexString> getStringDataLayout() {
return mixedSectionOffsets.getStringData();
}
@Override
public Collection<DexTypeList> getTypeListLayout() {
return mixedSectionOffsets.getTypeLists();
}
}