Initial strategy for emitting dex data section in startup order
Change-Id: Iddde05275d861cdce029cb597e60952231395bd4
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index 62909df..d2cff23 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -150,9 +150,15 @@
}
@Override
- public boolean setAnnotationsDirectoryForClass(DexProgramClass clazz,
- DexAnnotationDirectory annotationDirectory) {
- return true;
+ public void setAnnotationsDirectoryForClass(
+ DexProgramClass clazz, DexAnnotationDirectory annotationDirectory) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void setStaticFieldValuesForClass(
+ DexProgramClass clazz, DexEncodedArray staticFieldValues) {
+ add(staticFieldValues);
}
}
@@ -523,7 +529,7 @@
timing.end();
timing.begin("Write bytes");
- ByteBufferResult result = writeDexFile(objectMapping, byteBufferProvider, timing);
+ ByteBufferResult result = writeDexFile(objectMapping, byteBufferProvider, virtualFile, timing);
ByteDataView data =
new ByteDataView(result.buffer.array(), result.buffer.arrayOffset(), result.length);
timing.end();
@@ -818,9 +824,12 @@
}
private ByteBufferResult writeDexFile(
- ObjectToOffsetMapping objectMapping, ByteBufferProvider provider, Timing timing) {
+ ObjectToOffsetMapping objectMapping,
+ ByteBufferProvider provider,
+ VirtualFile virtualFile,
+ Timing timing) {
FileWriter fileWriter =
- new FileWriter(appView, provider, objectMapping, desugaredLibraryCodeToKeep);
+ new FileWriter(appView, provider, objectMapping, desugaredLibraryCodeToKeep, virtualFile);
// Collect the non-fixed sections.
timing.time("collect", fileWriter::collect);
// Generate and write the bytes.
diff --git a/src/main/java/com/android/tools/r8/dex/DefaultMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/DefaultMixedSectionLayoutStrategy.java
index 311a423..dee0f55 100644
--- a/src/main/java/com/android/tools/r8/dex/DefaultMixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/DefaultMixedSectionLayoutStrategy.java
@@ -28,8 +28,8 @@
public class DefaultMixedSectionLayoutStrategy extends MixedSectionLayoutStrategy {
- private final AppView<?> appView;
- private final MixedSectionOffsets mixedSectionOffsets;
+ final AppView<?> appView;
+ final MixedSectionOffsets mixedSectionOffsets;
public DefaultMixedSectionLayoutStrategy(
AppView<?> appView, MixedSectionOffsets mixedSectionOffsets) {
@@ -64,9 +64,13 @@
@Override
public Collection<ProgramMethod> getCodeLayout() {
+ return getCodeLayoutForClasses(mixedSectionOffsets.getClassesWithData());
+ }
+
+ final Collection<ProgramMethod> getCodeLayoutForClasses(Collection<DexProgramClass> classes) {
ProgramMethodMap<String> codeToSignatureMap = ProgramMethodMap.create();
List<ProgramMethod> codesSorted = new ArrayList<>();
- for (DexProgramClass clazz : mixedSectionOffsets.getClassesWithData()) {
+ for (DexProgramClass clazz : classes) {
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index bc3a515..a939e1a 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -95,13 +95,14 @@
private final DexOutputBuffer dest;
private final MixedSectionOffsets mixedSectionOffsets;
private final CodeToKeep desugaredLibraryCodeToKeep;
- private final Map<DexProgramClass, DexEncodedArray> staticFieldValues = new IdentityHashMap<>();
+ private final VirtualFile virtualFile;
public FileWriter(
AppView<?> appView,
ByteBufferProvider provider,
ObjectToOffsetMapping mapping,
- CodeToKeep desugaredLibraryCodeToKeep) {
+ CodeToKeep desugaredLibraryCodeToKeep,
+ VirtualFile virtualFile) {
this.appView = appView;
this.graphLens = appView.graphLens();
this.mapping = mapping;
@@ -109,6 +110,7 @@
this.dest = new DexOutputBuffer(provider);
this.mixedSectionOffsets = new MixedSectionOffsets(options);
this.desugaredLibraryCodeToKeep = desugaredLibraryCodeToKeep;
+ this.virtualFile = virtualFile;
}
private NamingLens getNamingLens() {
@@ -166,7 +168,7 @@
// Sort the codes first, as their order might impact size due to alignment constraints.
MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
- new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets);
+ MixedSectionLayoutStrategy.create(appView, mixedSectionOffsets, virtualFile);
Collection<ProgramMethod> codes = mixedSectionLayoutStrategy.getCodeLayout();
// Output the debug_info_items first, as they have no dependencies.
@@ -445,7 +447,6 @@
private void writeClassDefItem(DexProgramClass clazz) {
desugaredLibraryCodeToKeep.recordHierarchyOf(clazz);
-
dest.putInt(mapping.getOffsetFor(clazz.type));
dest.putInt(clazz.accessFlags.getAsDexAccessFlags());
dest.putInt(
@@ -456,7 +457,8 @@
dest.putInt(mixedSectionOffsets.getOffsetForAnnotationsDirectory(clazz));
dest.putInt(
clazz.hasMethodsOrFields() ? mixedSectionOffsets.getOffsetFor(clazz) : Constants.NO_OFFSET);
- dest.putInt(mixedSectionOffsets.getOffsetFor(staticFieldValues.get(clazz)));
+ dest.putInt(
+ mixedSectionOffsets.getOffsetFor(mixedSectionOffsets.getStaticFieldValuesForClass(clazz)));
}
private void writeDebugItem(DexDebugInfoForWriting debugInfo) {
@@ -665,8 +667,7 @@
// here.
DexEncodedArray staticValues = clazz.computeStaticValuesArray(getNamingLens());
if (staticValues != null) {
- staticFieldValues.put(clazz, staticValues);
- mixedSectionOffsets.add(staticValues);
+ mixedSectionOffsets.setStaticFieldValuesForClass(clazz, staticValues);
}
}
@@ -1070,7 +1071,9 @@
= createObject2IntMap();
private final Reference2IntMap<DexProgramClass> classesWithData = createReference2IntMap();
private final Object2IntMap<DexEncodedArray> encodedArrays = createObject2IntMap();
- private final Map<DexProgramClass, DexAnnotationDirectory> clazzToAnnotationDirectory =
+ private final Map<DexProgramClass, DexAnnotationDirectory> classToAnnotationDirectory =
+ new IdentityHashMap<>();
+ private final Map<DexProgramClass, DexEncodedArray> classToStaticFieldValues =
new IdentityHashMap<>();
private final AndroidApiLevel minApiLevel;
@@ -1164,11 +1167,19 @@
}
@Override
- public boolean setAnnotationsDirectoryForClass(DexProgramClass clazz,
- DexAnnotationDirectory annotationDirectory) {
- DexAnnotationDirectory previous = clazzToAnnotationDirectory.put(clazz, annotationDirectory);
+ public void setAnnotationsDirectoryForClass(
+ DexProgramClass clazz, DexAnnotationDirectory annotationDirectory) {
+ DexAnnotationDirectory previous = classToAnnotationDirectory.put(clazz, annotationDirectory);
assert previous == null;
- return add(annotationDirectories, annotationDirectory);
+ add(annotationDirectories, annotationDirectory);
+ }
+
+ @Override
+ public void setStaticFieldValuesForClass(
+ DexProgramClass clazz, DexEncodedArray staticFieldValues) {
+ DexEncodedArray previous = classToStaticFieldValues.put(clazz, staticFieldValues);
+ assert previous == null;
+ add(staticFieldValues);
}
public boolean add(DexString string) {
@@ -1256,12 +1267,11 @@
return lookup(debugInfo, debugInfos);
}
-
public int getOffsetForAnnotationsDirectory(DexProgramClass clazz) {
if (!clazz.hasClassOrMemberAnnotations()) {
return Constants.NO_OFFSET;
}
- int offset = annotationDirectories.getInt(clazzToAnnotationDirectory.get(clazz));
+ int offset = annotationDirectories.getInt(getAnnotationDirectoryForClass(clazz));
assert offset != NOT_KNOWN;
return offset;
}
@@ -1344,6 +1354,14 @@
assert offset != 0 && !annotationSetRefList.isEmpty();
setOffsetFor(annotationSetRefList, offset, annotationSetRefLists);
}
+
+ DexAnnotationDirectory getAnnotationDirectoryForClass(DexProgramClass clazz) {
+ return classToAnnotationDirectory.get(clazz);
+ }
+
+ DexEncodedArray getStaticFieldValuesForClass(DexProgramClass clazz) {
+ return classToStaticFieldValues.get(clazz);
+ }
}
private class ProgramClassDependencyCollector extends ProgramClassVisitor {
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
index f0c4c06..d80b036 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionCollection.java
@@ -110,10 +110,16 @@
/**
* Adds the given annotation directory to the collection.
*
- * Add a dependency between the clazz and the annotation directory.
- *
- * @return true if the item was not added before
+ * <p>Adds a dependency between the clazz and the annotation directory.
*/
- public abstract boolean setAnnotationsDirectoryForClass(DexProgramClass clazz,
- DexAnnotationDirectory annotationDirectory);
+ public abstract void setAnnotationsDirectoryForClass(
+ DexProgramClass clazz, DexAnnotationDirectory annotationDirectory);
+
+ /**
+ * Adds the given static field values array to the collection.
+ *
+ * <p>Adds a dependency between the clazz and the static field values array.
+ */
+ public abstract void setStaticFieldValuesForClass(
+ DexProgramClass clazz, DexEncodedArray staticFieldValues);
}
diff --git a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
index 350db0f..5454b0f 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -4,6 +4,9 @@
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.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationDirectory;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -17,6 +20,22 @@
public abstract class MixedSectionLayoutStrategy {
+ public static MixedSectionLayoutStrategy create(
+ AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
+ StartupOrder startupOrderForWriting =
+ virtualFile.getId() == 0 && appView.hasClassHierarchy()
+ ? appView
+ .appInfoWithClassHierarchy()
+ .getStartupOrder()
+ .toStartupOrderForWriting(appView)
+ : StartupOrder.empty();
+ if (startupOrderForWriting.isEmpty()) {
+ return new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets);
+ }
+ return new StartupMixedSectionLayoutStrategy(
+ appView, mixedSectionOffsets, startupOrderForWriting, virtualFile);
+ }
+
public abstract Collection<DexAnnotation> getAnnotationLayout();
public abstract Collection<DexAnnotationDirectory> getAnnotationDirectoryLayout();
diff --git a/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
new file mode 100644
index 0000000..11a22f6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/StartupMixedSectionLayoutStrategy.java
@@ -0,0 +1,202 @@
+// 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.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.DexEncodedMethod;
+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.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;
+import java.util.Set;
+
+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);
+ }
+
+ 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 (DexType startupClass : startupOrderForWriting.getClasses()) {
+ DexProgramClass definition = virtualFileDefinitions.get(startupClass);
+ if (definition != null) {
+ definition.collectIndexedItems(appView, indexedItemCollection, 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() {
+ Set<DexProgramClass> nonStartupClasses =
+ new LinkedHashSet<>(mixedSectionOffsets.getClassesWithData());
+ nonStartupClasses.removeIf(clazz -> startupOrderForWriting.contains(clazz.getType()));
+ return amendStartupLayout(codeLayout, super.getCodeLayoutForClasses(nonStartupClasses));
+ }
+
+ @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 {
+
+ @Override
+ public boolean addClass(DexProgramClass clazz) {
+ classDataLayout.add(clazz);
+ typeListLayout.add(clazz.getInterfaces());
+ clazz.forEachProgramMethodMatching(DexEncodedMethod::hasCode, codeLayout::add);
+ DexAnnotationDirectory annotationDirectory =
+ mixedSectionOffsets.getAnnotationDirectoryForClass(clazz);
+ if (annotationDirectory != null) {
+ annotationDirectoryLayout.add(annotationDirectory);
+ annotationDirectory.visitAnnotations(
+ annotationLayout::add, annotationSetLayout::add, annotationSetRefListLayout::add);
+ }
+ DexEncodedArray staticFieldValues = mixedSectionOffsets.getStaticFieldValuesForClass(clazz);
+ if (staticFieldValues != null) {
+ encodedArrayLayout.add(staticFieldValues);
+ }
+ return true;
+ }
+
+ @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) {
+ typeListLayout.add(proto.getParameters());
+ return true;
+ }
+
+ @Override
+ public boolean addType(DexType type) {
+ return true;
+ }
+
+ @Override
+ public boolean addCallSite(DexCallSite callSite) {
+ encodedArrayLayout.add(callSite.getEncodedArray());
+ return true;
+ }
+
+ @Override
+ public boolean addMethodHandle(DexMethodHandle methodHandle) {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
index 825f95f..7279ee7 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/EmptyStartupOrder.java
@@ -4,19 +4,43 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
+import java.util.Collection;
+import java.util.Collections;
public class EmptyStartupOrder extends StartupOrder {
EmptyStartupOrder() {}
@Override
+ public boolean contains(DexType type) {
+ return false;
+ }
+
+ @Override
+ public Collection<DexType> getClasses() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+
+ @Override
public EmptyStartupOrder rewrittenWithLens(GraphLens graphLens) {
return this;
}
@Override
+ public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
+ return this;
+ }
+
+ @Override
public EmptyStartupOrder withoutPrunedItems(PrunedItems prunedItems) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
index d181e4a..0427fc3 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/NonEmptyStartupOrder.java
@@ -4,10 +4,17 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
public class NonEmptyStartupOrder extends StartupOrder {
@@ -19,6 +26,21 @@
}
@Override
+ public boolean contains(DexType type) {
+ return startupClasses.contains(type);
+ }
+
+ @Override
+ public Collection<DexType> getClasses() {
+ return startupClasses;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
public StartupOrder rewrittenWithLens(GraphLens graphLens) {
LinkedHashSet<DexType> rewrittenStartupClasses = new LinkedHashSet<>(startupClasses.size());
for (DexType startupClass : startupClasses) {
@@ -29,6 +51,84 @@
}
@Override
+ public StartupOrder toStartupOrderForWriting(AppView<?> appView) {
+ LinkedHashSet<DexType> rewrittenStartupClasses = new LinkedHashSet<>(startupClasses.size());
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
+ new IdentityHashMap<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (appView.getSyntheticItems().isSyntheticClass(clazz)) {
+ for (DexType synthesizingContextType :
+ appView.getSyntheticItems().getSynthesizingContextTypes(clazz.getType())) {
+ syntheticContextsToSyntheticClasses
+ .computeIfAbsent(synthesizingContextType, ignoreKey -> new ArrayList<>())
+ .add(clazz);
+ }
+ }
+ }
+ for (DexType startupClass : startupClasses) {
+ addClassAndParentClasses(
+ startupClass, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
+ }
+ return createNonEmpty(rewrittenStartupClasses);
+ }
+
+ private static boolean addClass(
+ DexProgramClass clazz, LinkedHashSet<DexType> rewrittenStartupClasses) {
+ return rewrittenStartupClasses.add(clazz.getType());
+ }
+
+ private static void addClassAndParentClasses(
+ DexType type,
+ LinkedHashSet<DexType> rewrittenStartupClasses,
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
+ AppView<?> appView) {
+ DexProgramClass definition = appView.app().programDefinitionFor(type);
+ if (definition != null) {
+ addClassAndParentClasses(
+ definition, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
+ }
+ }
+
+ private static void addClassAndParentClasses(
+ DexProgramClass clazz,
+ LinkedHashSet<DexType> rewrittenStartupClasses,
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
+ AppView<?> appView) {
+ if (addClass(clazz, rewrittenStartupClasses)) {
+ addSyntheticClassesAndParentClasses(
+ clazz, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
+ addParentClasses(
+ clazz, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
+ }
+ }
+
+ private static void addSyntheticClassesAndParentClasses(
+ DexProgramClass clazz,
+ LinkedHashSet<DexType> rewrittenStartupClasses,
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
+ AppView<?> appView) {
+ List<DexProgramClass> derivedClasses =
+ syntheticContextsToSyntheticClasses.remove(clazz.getType());
+ if (derivedClasses != null) {
+ for (DexProgramClass derivedClass : derivedClasses) {
+ addClassAndParentClasses(
+ derivedClass, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView);
+ }
+ }
+ }
+
+ private static void addParentClasses(
+ DexProgramClass clazz,
+ LinkedHashSet<DexType> rewrittenStartupClasses,
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses,
+ AppView<?> appView) {
+ clazz.forEachImmediateSupertype(
+ supertype ->
+ addClassAndParentClasses(
+ supertype, rewrittenStartupClasses, syntheticContextsToSyntheticClasses, appView));
+ }
+
+ @Override
public StartupOrder withoutPrunedItems(PrunedItems prunedItems) {
LinkedHashSet<DexType> rewrittenStartupClasses = new LinkedHashSet<>(startupClasses.size());
for (DexType startupClass : startupClasses) {
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
index 0c1425c..7e78009 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import java.io.IOException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@@ -46,15 +47,18 @@
public static StartupConfiguration createStartupConfiguration(
DexItemFactory dexItemFactory, Reporter reporter) {
String propertyValue = System.getProperty("com.android.tools.r8.startup.config");
- if (propertyValue == null) {
- return null;
- }
+ return propertyValue != null
+ ? createStartupConfigurationFromFile(dexItemFactory, reporter, Paths.get(propertyValue))
+ : null;
+ }
+ public static StartupConfiguration createStartupConfigurationFromFile(
+ DexItemFactory dexItemFactory, Reporter reporter, Path path) {
reporter.warning("Use of startupconfig is experimental");
List<String> startupDescriptors;
try {
- startupDescriptors = FileUtils.readAllLines(Paths.get(propertyValue));
+ startupDescriptors = FileUtils.readAllLines(path);
} catch (IOException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e));
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
index eb77225..da68a15 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOrder.java
@@ -4,9 +4,12 @@
package com.android.tools.r8.experimental.startup;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Collection;
import java.util.LinkedHashSet;
public abstract class StartupOrder {
@@ -29,7 +32,15 @@
return new EmptyStartupOrder();
}
+ public abstract boolean contains(DexType type);
+
+ public abstract Collection<DexType> getClasses();
+
+ public abstract boolean isEmpty();
+
public abstract StartupOrder rewrittenWithLens(GraphLens graphLens);
+ public abstract StartupOrder toStartupOrderForWriting(AppView<?> appView);
+
public abstract StartupOrder withoutPrunedItems(PrunedItems prunedItems);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 57251a2..6dd2ee7 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -249,8 +249,12 @@
return appInfo;
}
+ public AppInfoWithClassHierarchy appInfoWithClassHierarchy() {
+ return hasClassHierarchy() ? appInfo.withClassHierarchy() : null;
+ }
+
public AppInfoWithLiveness appInfoWithLiveness() {
- return appInfo.hasLiveness() ? appInfo.withLiveness() : null;
+ return hasLiveness() ? appInfo.withLiveness() : null;
}
public AppInfoWithClassHierarchy appInfoForDesugaring() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
index d2d763e..c0060f3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationDirectory.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.utils.structural.CompareToVisitor;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
public class DexAnnotationDirectory extends DexItem {
@@ -41,6 +42,46 @@
}
}
+ public void visitAnnotations(
+ Consumer<DexAnnotation> annotationConsumer,
+ Consumer<DexAnnotationSet> annotationSetConsumer,
+ Consumer<ParameterAnnotationsList> parameterAnnotationsListConsumer) {
+ visitAnnotationSet(clazz.annotations(), annotationConsumer, annotationSetConsumer);
+ clazz.forEachField(
+ field ->
+ visitAnnotationSet(field.annotations(), annotationConsumer, annotationSetConsumer));
+ clazz.forEachMethod(
+ method -> {
+ visitAnnotationSet(method.annotations(), annotationConsumer, annotationSetConsumer);
+ visitParameterAnnotationsList(
+ method.getParameterAnnotations(),
+ annotationConsumer,
+ annotationSetConsumer,
+ parameterAnnotationsListConsumer);
+ });
+ }
+
+ private void visitAnnotationSet(
+ DexAnnotationSet annotationSet,
+ Consumer<DexAnnotation> annotationConsumer,
+ Consumer<DexAnnotationSet> annotationSetConsumer) {
+ annotationSetConsumer.accept(annotationSet);
+ for (DexAnnotation annotation : annotationSet.getAnnotations()) {
+ annotationConsumer.accept(annotation);
+ }
+ }
+
+ private void visitParameterAnnotationsList(
+ ParameterAnnotationsList parameterAnnotationsList,
+ Consumer<DexAnnotation> annotationConsumer,
+ Consumer<DexAnnotationSet> annotationSetConsumer,
+ Consumer<ParameterAnnotationsList> parameterAnnotationsListConsumer) {
+ parameterAnnotationsListConsumer.accept(parameterAnnotationsList);
+ for (DexAnnotationSet annotationSet : parameterAnnotationsList.getAnnotationSets()) {
+ visitAnnotationSet(annotationSet, annotationConsumer, annotationSetConsumer);
+ }
+ }
+
public DexAnnotationSet getClazzAnnotations() {
return clazz.annotations();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
index 38eb26e..bcddd66 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSet.java
@@ -65,6 +65,10 @@
return this;
}
+ public DexAnnotation[] getAnnotations() {
+ return annotations;
+ }
+
@Override
public StructuralMapping<DexAnnotationSet> getStructuralMapping() {
return DexAnnotationSet::specify;
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index f820b52..e3b49d1 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -173,7 +173,7 @@
@Override
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
- mixedItems.add(getEncodedArray());
+ getEncodedArray().collectMixedSectionItems(mixedItems);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
index 760b11d..5f7c865 100644
--- a/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
+++ b/src/main/java/com/android/tools/r8/graph/ParameterAnnotationsList.java
@@ -132,6 +132,10 @@
mixedItems.add(this);
}
+ public DexAnnotationSet[] getAnnotationSets() {
+ return values;
+ }
+
public boolean isEmpty() {
return values.length == 0;
}
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 2f55680..bf80d7a 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -45,6 +45,13 @@
return map;
}
+ public static <K, V> IdentityHashMap<K, V> newIdentityHashMap(
+ BiForEachable<K, V> forEachable, int capacity) {
+ IdentityHashMap<K, V> map = new IdentityHashMap<>(capacity);
+ forEachable.forEach(map::put);
+ return map;
+ }
+
public static <T> void removeIdentityMappings(Map<T, T> map) {
map.entrySet().removeIf(entry -> entry.getKey() == entry.getValue());
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index f42a8cd..0787662 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.utils.SetUtils;
+import com.google.common.collect.Iterables;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Iterator;
@@ -18,7 +19,8 @@
import java.util.function.Supplier;
import java.util.stream.Stream;
-public abstract class DexClassAndMethodSetBase<T extends DexClassAndMethod> implements Iterable<T> {
+public abstract class DexClassAndMethodSetBase<T extends DexClassAndMethod>
+ implements Collection<T> {
protected final Map<DexMethod, T> backing;
protected final Supplier<? extends Map<DexMethod, T>> backingFactory;
@@ -33,14 +35,20 @@
this.backingFactory = backingFactory;
}
+ @Override
public boolean add(T method) {
T existing = backing.put(method.getReference(), method);
assert existing == null || existing.isStructurallyEqualTo(method);
return existing == null;
}
- public void addAll(Iterable<T> methods) {
- methods.forEach(this::add);
+ @Override
+ public boolean addAll(Collection<? extends T> methods) {
+ boolean changed = false;
+ for (T method : methods) {
+ changed |= add(method);
+ }
+ return changed;
}
public T get(DexMethod method) {
@@ -51,6 +59,15 @@
return iterator().next();
}
+ @Override
+ public boolean contains(Object o) {
+ if (o instanceof DexClassAndMethod) {
+ DexClassAndMethod method = (DexClassAndMethod) o;
+ return contains(method.getReference());
+ }
+ return false;
+ }
+
public boolean contains(DexMethod method) {
return backing.containsKey(method);
}
@@ -63,10 +80,17 @@
return backing.containsKey(method.getReference());
}
+ @Override
+ public boolean containsAll(Collection<?> collection) {
+ return Iterables.all(collection, this::contains);
+ }
+
+ @Override
public void clear() {
backing.clear();
}
+ @Override
public boolean isEmpty() {
return backing.isEmpty();
}
@@ -76,6 +100,15 @@
return backing.values().iterator();
}
+ @Override
+ public boolean remove(Object o) {
+ if (o instanceof DexClassAndMethod) {
+ DexClassAndMethod method = (DexClassAndMethod) o;
+ return remove(method.getReference());
+ }
+ return false;
+ }
+
public boolean remove(DexMethod method) {
T existing = backing.remove(method);
return existing != null;
@@ -85,18 +118,45 @@
return remove(method.getReference());
}
+ @Override
+ public boolean removeAll(Collection<?> collection) {
+ boolean changed = false;
+ for (Object o : collection) {
+ changed |= remove(o);
+ }
+ return changed;
+ }
+
+ @Override
public boolean removeIf(Predicate<? super T> predicate) {
return backing.values().removeIf(predicate);
}
+ @Override
+ public boolean retainAll(Collection<?> collection) {
+ return backing.values().retainAll(collection);
+ }
+
+ @Override
public int size() {
return backing.size();
}
+ @Override
public Stream<T> stream() {
return backing.values().stream();
}
+ @Override
+ public Object[] toArray() {
+ return backing.values().toArray();
+ }
+
+ @Override
+ public <S> S[] toArray(S[] ss) {
+ return backing.values().toArray(ss);
+ }
+
public Collection<T> toCollection() {
return backing.values();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LinkedProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/LinkedProgramMethodSet.java
new file mode 100644
index 0000000..b323e7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/LinkedProgramMethodSet.java
@@ -0,0 +1,26 @@
+// 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.utils.collections;
+
+import java.util.LinkedHashMap;
+
+public class LinkedProgramMethodSet extends ProgramMethodSet {
+
+ LinkedProgramMethodSet() {
+ super(LinkedProgramMethodSet::createBacking, createBacking());
+ }
+
+ LinkedProgramMethodSet(int capacity) {
+ super(LinkedProgramMethodSet::createBacking, createBacking(capacity));
+ }
+
+ private static <K, V> LinkedHashMap<K, V> createBacking() {
+ return new LinkedHashMap<>();
+ }
+
+ private static <K, V> LinkedHashMap<K, V> createBacking(int capacity) {
+ return new LinkedHashMap<>(capacity);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
index 12deaa2..81bb460 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodSet.java
@@ -13,7 +13,6 @@
import com.android.tools.r8.utils.ForEachable;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@@ -62,8 +61,12 @@
return new ProgramMethodSet(ConcurrentHashMap::new);
}
- public static ProgramMethodSet createLinked() {
- return new ProgramMethodSet(LinkedHashMap::new);
+ public static LinkedProgramMethodSet createLinked() {
+ return new LinkedProgramMethodSet();
+ }
+
+ public static LinkedProgramMethodSet createLinked(int capacity) {
+ return new LinkedProgramMethodSet(capacity);
}
public static ProgramMethodSet empty() {
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 2530c29..b5e2af2 100644
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -98,12 +98,12 @@
def ensure_screen_on(device_id=None):
if get_screen_state(device_id).is_off():
toggle_screen(device_id)
- assert adb_utils.get_screen_state(options.device_id).is_on()
+ assert get_screen_state(device_id).is_on()
def ensure_screen_off(device_id=None):
if get_screen_state(device_id).is_on():
toggle_screen(device_id)
- assert adb_utils.get_screen_state(options.device_id).is_off()
+ assert get_screen_state(device_id).is_off()
def force_compilation(app_id, device_id=None):
print('Applying AOT (full)')
diff --git a/tools/startup/measure_startup.py b/tools/startup/measure_startup.py
index 3a56bac..04076ec 100755
--- a/tools/startup/measure_startup.py
+++ b/tools/startup/measure_startup.py
@@ -100,6 +100,8 @@
time.sleep(options.cooldown)
teardown_options = adb_utils.prepare_for_interaction_with_device(
options.device_id, options.device_pin)
+ else:
+ teardown_options = None
# Prelaunch for hot startup.
if options.hot_startup: