blob: ffbbc4ff43486fd807fa98b54f6fcf4bb8d2e4c0 [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.experimental.startup.StartupOrder;
import com.android.tools.r8.experimental.startup.profile.StartupClass;
import com.android.tools.r8.experimental.startup.profile.StartupItem;
import com.android.tools.r8.experimental.startup.profile.StartupMethod;
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.DexCallSite;
import com.android.tools.r8.graph.DexEncodedArray;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexWritableCode;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.collections.LinkedProgramMethodSet;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
public class StartupMixedSectionLayoutStrategy extends DefaultMixedSectionLayoutStrategy {
private final StartupOrder startupOrderForWriting;
private final LinkedHashSet<DexAnnotation> annotationLayout;
private final LinkedHashSet<DexAnnotationDirectory> annotationDirectoryLayout;
private final LinkedHashSet<DexAnnotationSet> annotationSetLayout;
private final LinkedHashSet<ParameterAnnotationsList> annotationSetRefListLayout;
private final LinkedHashSet<DexProgramClass> classDataLayout;
private final LinkedProgramMethodSet codeLayout;
private final LinkedHashSet<DexEncodedArray> encodedArrayLayout;
private final LinkedHashSet<DexString> stringDataLayout;
private final LinkedHashSet<DexTypeList> typeListLayout;
public StartupMixedSectionLayoutStrategy(
AppView<?> appView,
MixedSectionOffsets mixedSectionOffsets,
StartupOrder startupOrderForWriting,
VirtualFile virtualFile) {
super(appView, mixedSectionOffsets);
this.startupOrderForWriting = startupOrderForWriting;
// Initialize startup layouts.
this.annotationLayout = new LinkedHashSet<>(mixedSectionOffsets.getAnnotations().size());
this.annotationDirectoryLayout =
new LinkedHashSet<>(mixedSectionOffsets.getAnnotationDirectories().size());
this.annotationSetLayout = new LinkedHashSet<>(mixedSectionOffsets.getAnnotationSets().size());
this.annotationSetRefListLayout =
new LinkedHashSet<>(mixedSectionOffsets.getAnnotationSetRefLists().size());
this.classDataLayout = new LinkedHashSet<>(mixedSectionOffsets.getClassesWithData().size());
this.codeLayout = ProgramMethodSet.createLinked(mixedSectionOffsets.getCodes().size());
this.encodedArrayLayout = new LinkedHashSet<>(mixedSectionOffsets.getEncodedArrays().size());
this.stringDataLayout = new LinkedHashSet<>(mixedSectionOffsets.getStringData().size());
this.typeListLayout = new LinkedHashSet<>(mixedSectionOffsets.getTypeLists().size());
// Add startup items to startup layouts.
collectStartupItems(virtualFile);
}
/** This adds all startup items to the startup layouts (i.e., the fields of this class). */
private void collectStartupItems(VirtualFile virtualFile) {
Map<DexType, DexProgramClass> virtualFileDefinitions =
MapUtils.newIdentityHashMap(
builder ->
virtualFile.classes().forEach(clazz -> builder.accept(clazz.getType(), clazz)),
virtualFile.classes().size());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView, true);
StartupIndexedItemCollection indexedItemCollection = new StartupIndexedItemCollection();
for (StartupItem startupItem : startupOrderForWriting.getItems()) {
startupItem.accept(
startupClass ->
collectStartupItems(startupClass, indexedItemCollection, virtualFileDefinitions),
startupMethod ->
collectStartupItems(
startupMethod, indexedItemCollection, virtualFileDefinitions, rewriter));
}
}
private void collectStartupItems(
StartupClass startupClass,
StartupIndexedItemCollection indexedItemCollection,
Map<DexType, DexProgramClass> virtualFileDefinitions) {
DexProgramClass definition = virtualFileDefinitions.get(startupClass.getReference());
if (definition != null) {
// Note that this must not call definition.collectIndexedItems, since that would collect all
// items from the class, and not only the startup items.
indexedItemCollection.addClass(definition);
// Collect the descriptor of the current type.
definition.getType().collectIndexedItems(appView, indexedItemCollection);
// Collect the descriptors (strings) of the supertypes.
definition.forEachImmediateSupertype(
supertype -> supertype.collectIndexedItems(appView, indexedItemCollection));
// TODO(b/238173796): Consider collecting the source file, the annotations, the enclosing
// method attribute, the inner class attribute, and the fields (i.e., annotations and static
// values).
}
}
private void collectStartupItems(
StartupMethod startupMethod,
StartupIndexedItemCollection indexedItemCollection,
Map<DexType, DexProgramClass> virtualFileDefinitions,
LensCodeRewriterUtils rewriter) {
DexMethod methodReference = startupMethod.getReference();
DexProgramClass holder = virtualFileDefinitions.get(methodReference.getHolderType());
ProgramMethod method = methodReference.lookupOnProgramClass(holder);
if (method != null) {
methodReference.collectIndexedItems(appView, indexedItemCollection);
if (indexedItemCollection.addCode(method)) {
DexWritableCode code = method.getDefinition().getCode().asDexWritableCode();
code.collectIndexedItems(appView, indexedItemCollection, method, rewriter);
}
}
}
private static <T> Collection<T> amendStartupLayout(
Collection<T> startupLayout, Collection<T> defaultLayout) {
startupLayout.addAll(defaultLayout);
return startupLayout;
}
@Override
public Collection<DexAnnotation> getAnnotationLayout() {
return amendStartupLayout(annotationLayout, super.getAnnotationLayout());
}
@Override
public Collection<DexAnnotationDirectory> getAnnotationDirectoryLayout() {
return amendStartupLayout(annotationDirectoryLayout, super.getAnnotationDirectoryLayout());
}
@Override
public Collection<DexAnnotationSet> getAnnotationSetLayout() {
return amendStartupLayout(annotationSetLayout, super.getAnnotationSetLayout());
}
@Override
public Collection<ParameterAnnotationsList> getAnnotationSetRefListLayout() {
return amendStartupLayout(annotationSetRefListLayout, super.getAnnotationSetRefListLayout());
}
@Override
public Collection<DexProgramClass> getClassDataLayout() {
return amendStartupLayout(classDataLayout, super.getClassDataLayout());
}
@Override
public Collection<ProgramMethod> getCodeLayout() {
return amendStartupLayout(codeLayout, super.getCodeLayout());
}
@Override
public Collection<DexEncodedArray> getEncodedArrayLayout() {
return amendStartupLayout(encodedArrayLayout, super.getEncodedArrayLayout());
}
@Override
public Collection<DexString> getStringDataLayout() {
return amendStartupLayout(stringDataLayout, super.getStringDataLayout());
}
@Override
public Collection<DexTypeList> getTypeListLayout() {
return amendStartupLayout(typeListLayout, super.getTypeListLayout());
}
private class StartupIndexedItemCollection implements IndexedItemCollection {
private void addAnnotation(DexAnnotation annotation) {
annotationLayout.add(annotation);
}
private void addAnnotationSet(DexAnnotationSet annotationSet) {
if (appView.options().canHaveDalvikEmptyAnnotationSetBug() || !annotationSet.isEmpty()) {
annotationSetLayout.add(annotationSet);
}
}
private void addAnnotationSetRefList(ParameterAnnotationsList annotationSetRefList) {
if (!annotationSetRefList.isEmpty()) {
annotationSetRefListLayout.add(annotationSetRefList);
}
}
@Override
public boolean addClass(DexProgramClass clazz) {
if (clazz.hasMethodsOrFields()) {
classDataLayout.add(clazz);
}
addTypeList(clazz.getInterfaces());
DexAnnotationDirectory annotationDirectory =
mixedSectionOffsets.getAnnotationDirectoryForClass(clazz);
if (annotationDirectory != null) {
annotationDirectoryLayout.add(annotationDirectory);
annotationDirectory.visitAnnotations(
this::addAnnotation, this::addAnnotationSet, this::addAnnotationSetRefList);
}
DexEncodedArray staticFieldValues = mixedSectionOffsets.getStaticFieldValuesForClass(clazz);
if (staticFieldValues != null) {
encodedArrayLayout.add(staticFieldValues);
}
return true;
}
public boolean addCode(ProgramMethod method) {
if (method.getDefinition().hasCode()) {
codeLayout.add(method);
return true;
}
return false;
}
@Override
public boolean addField(DexField field) {
return true;
}
@Override
public boolean addMethod(DexMethod method) {
return true;
}
@Override
public boolean addString(DexString string) {
return stringDataLayout.add(string);
}
@Override
public boolean addProto(DexProto proto) {
addTypeList(proto.getParameters());
return true;
}
@Override
public boolean addType(DexType type) {
return true;
}
private void addTypeList(DexTypeList typeList) {
if (!typeList.isEmpty()) {
typeListLayout.add(typeList);
}
}
@Override
public boolean addCallSite(DexCallSite callSite) {
encodedArrayLayout.add(callSite.getEncodedArray());
return true;
}
@Override
public boolean addMethodHandle(DexMethodHandle methodHandle) {
return true;
}
}
}