Support for writing app using startup list in D8
Bug: b/242570743
Change-Id: I54cbca67e3adfa5f9bc6df2c47bc2d69b51ef778
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 03ec1dd..8be1456 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
-import com.android.tools.r8.experimental.startup.StartupInstrumentation;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentation;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
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 3cba5ca..4932691 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.dex.VirtualFile.ItemUseInfo;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.startup.StartupCompleteness;
+import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -217,7 +218,16 @@
&& options.enableMainDexListCheck) {
distributor = new VirtualFile.MonoDexDistributor(this, classes, options);
} else {
- distributor = new VirtualFile.FillFilesDistributor(this, classes, options, executorService);
+ // Retrieve the startup order for writing the app. In R8, the startup order is created
+ // up-front to guide optimizations through-out the compilation. In D8, the startup
+ // order is only used for writing the app, so we create it here for the first time.
+ StartupOrder startupOrder =
+ appView.appInfo().hasClassHierarchy()
+ ? appView.appInfoWithClassHierarchy().getStartupOrder()
+ : StartupOrder.createInitialStartupOrder(options);
+ distributor =
+ new VirtualFile.FillFilesDistributor(
+ this, classes, options, executorService, startupOrder);
}
List<VirtualFile> virtualFiles = distributor.run();
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 2381e6f..13a4a2c 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -22,15 +22,16 @@
public static MixedSectionLayoutStrategy create(
AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
- StartupOrder startupOrderForWriting =
- appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
- && virtualFile.getId() == 0
- && appView.hasClassHierarchy()
- ? appView
- .appInfoWithClassHierarchy()
- .getStartupOrder()
- .toStartupOrderForWriting(appView)
- : StartupOrder.empty();
+ StartupOrder startupOrderForWriting;
+ if (virtualFile.getStartupOrder().isEmpty()) {
+ startupOrderForWriting = StartupOrder.empty();
+ } else {
+ assert virtualFile.getId() == 0;
+ startupOrderForWriting =
+ appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
+ ? virtualFile.getStartupOrder().toStartupOrderForWriting(appView)
+ : StartupOrder.empty();
+ }
MixedSectionLayoutStrategy mixedSectionLayoutStrategy =
startupOrderForWriting.isEmpty()
? new DefaultMixedSectionLayoutStrategy(appView, mixedSectionOffsets)
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 1fa09a7..785bfd2 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.dex;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.google.common.base.Predicates.alwaysFalse;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.debuginfo.DebugRepresentation;
@@ -74,33 +73,35 @@
public final VirtualFileIndexedItemCollection indexedItems;
private final IndexedItemTransaction transaction;
private final FeatureSplit featureSplit;
+ private final StartupOrder startupOrder;
private final DexString primaryClassDescriptor;
private DebugRepresentation debugRepresentation;
VirtualFile(int id, AppView<?> appView) {
- this(id, appView, null, null);
+ this(id, appView, null, null, StartupOrder.empty());
}
VirtualFile(
int id,
AppView<?> appView,
FeatureSplit featureSplit) {
- this(id, appView, null, featureSplit);
+ this(id, appView, null, featureSplit, StartupOrder.empty());
}
private VirtualFile(
int id,
AppView<?> appView,
DexProgramClass primaryClass) {
- this(id, appView, primaryClass, null);
+ this(id, appView, primaryClass, null, StartupOrder.empty());
}
private VirtualFile(
int id,
AppView<?> appView,
DexProgramClass primaryClass,
- FeatureSplit featureSplit) {
+ FeatureSplit featureSplit,
+ StartupOrder startupOrder) {
this.id = id;
this.indexedItems = new VirtualFileIndexedItemCollection(appView);
this.transaction = new IndexedItemTransaction(indexedItems, appView);
@@ -109,6 +110,7 @@
? null
: appView.getNamingLens().lookupClassDescriptor(primaryClass.type);
this.featureSplit = featureSplit;
+ this.startupOrder = startupOrder;
}
public int getId() {
@@ -128,6 +130,10 @@
return featureSplit;
}
+ public StartupOrder getStartupOrder() {
+ return startupOrder;
+ }
+
public String getPrimaryClassDescriptor() {
return primaryClassDescriptor == null ? null : primaryClassDescriptor.toString();
}
@@ -352,13 +358,17 @@
protected final InternalOptions options;
DistributorBase(
- ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
+ ApplicationWriter writer,
+ Collection<DexProgramClass> classes,
+ InternalOptions options,
+ StartupOrder startupOrder) {
super(writer);
this.options = options;
this.classes = SetUtils.newIdentityHashSet(classes);
- // Create the primary dex file. The distribution will add more if needed.
- mainDexFile = new VirtualFile(0, appView);
+ // Create the primary dex file. The distribution will add more if needed. We use the startup
+ // order (if any) to guide the layout of the primary dex file.
+ mainDexFile = new VirtualFile(0, appView, null, null, startupOrder);
assert virtualFiles.isEmpty();
virtualFiles.add(mainDexFile);
addMarkers(mainDexFile);
@@ -429,7 +439,7 @@
}
protected void addFeatureSplitFiles(
- Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses) {
+ Map<FeatureSplit, Set<DexProgramClass>> featureSplitClasses, StartupOrder startupOrder) {
if (featureSplitClasses.isEmpty()) {
return;
}
@@ -452,6 +462,7 @@
appView,
featureSplitSetEntry.getValue(),
originalNames,
+ startupOrder,
nextFileId)
.run();
}
@@ -459,15 +470,19 @@
}
public static class FillFilesDistributor extends DistributorBase {
+
private final ExecutorService executorService;
+ private final StartupOrder startupOrder;
FillFilesDistributor(
ApplicationWriter writer,
Collection<DexProgramClass> classes,
InternalOptions options,
- ExecutorService executorService) {
- super(writer, classes, options);
+ ExecutorService executorService,
+ StartupOrder startupOrder) {
+ super(writer, classes, options, startupOrder);
this.executorService = executorService;
+ this.startupOrder = startupOrder;
}
@Override
@@ -508,10 +523,16 @@
.distribute();
} else {
new PackageSplitPopulator(
- virtualFiles, filesForDistribution, appView, classes, originalNames, nextFileId)
+ virtualFiles,
+ filesForDistribution,
+ appView,
+ classes,
+ originalNames,
+ startupOrder,
+ nextFileId)
.run();
}
- addFeatureSplitFiles(featureSplitClasses);
+ addFeatureSplitFiles(featureSplitClasses, startupOrder);
assert totalClassNumber == virtualFiles.stream().mapToInt(dex -> dex.classes().size()).sum();
return virtualFiles;
@@ -521,7 +542,7 @@
public static class MonoDexDistributor extends DistributorBase {
MonoDexDistributor(
ApplicationWriter writer, Collection<DexProgramClass> classes, InternalOptions options) {
- super(writer, classes, options);
+ super(writer, classes, options, StartupOrder.empty());
}
@Override
@@ -537,7 +558,7 @@
if (options.featureSplitConfiguration != null) {
if (!featureSplitClasses.isEmpty()) {
// TODO(141334414): Figure out if we allow multidex in features even when mono-dexing
- addFeatureSplitFiles(featureSplitClasses);
+ addFeatureSplitFiles(featureSplitClasses, StartupOrder.empty());
}
}
return virtualFiles;
@@ -1242,12 +1263,13 @@
public static PackageSplitClassPartioning create(
Collection<DexProgramClass> classes,
- AppView<?> appView,
- Map<DexProgramClass, String> originalNames) {
+ Map<DexProgramClass, String> originalNames,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
return create(
classes,
getClassesByPackageComparator(originalNames),
- getStartupClassPredicate(appView));
+ getStartupClassPredicate(startupOrder, syntheticItems));
}
private static PackageSplitClassPartioning create(
@@ -1297,12 +1319,8 @@
};
}
- private static Predicate<DexProgramClass> getStartupClassPredicate(AppView<?> appView) {
- if (!appView.hasClassHierarchy()) {
- return alwaysFalse();
- }
- StartupOrder startupOrder = appView.appInfoWithClassHierarchy().getStartupOrder();
- SyntheticItems syntheticItems = appView.getSyntheticItems();
+ private static Predicate<DexProgramClass> getStartupClassPredicate(
+ StartupOrder startupOrder, SyntheticItems syntheticItems) {
return clazz -> startupOrder.contains(clazz.getType(), syntheticItems);
}
@@ -1339,8 +1357,11 @@
AppView<?> appView,
Collection<DexProgramClass> classes,
Map<DexProgramClass, String> originalNames,
+ StartupOrder startupOrder,
IntBox nextFileId) {
- this.classPartioning = PackageSplitClassPartioning.create(classes, appView, originalNames);
+ this.classPartioning =
+ PackageSplitClassPartioning.create(
+ classes, originalNames, startupOrder, appView.getSyntheticItems());
this.originalNames = originalNames;
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
index 8d75d7c..ca0b88f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.java
@@ -31,7 +31,7 @@
this.startupOrder =
appView.hasClassHierarchy()
? appView.appInfoWithClassHierarchy().getStartupOrder()
- : StartupOrder.empty();
+ : StartupOrder.createInitialStartupOrder(appView.options());
}
/**
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
index f1b771c..05fd544 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupOptions.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.experimental.startup;
-import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
public class StartupOptions {
@@ -39,76 +38,14 @@
"com.android.tools.r8.startup.completenesscheck", false);
/**
- * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
- * it has been executed.
- *
- * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
- * app.
- */
- private boolean enableStartupInstrumentation =
- parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.instrument", false);
-
- /**
* When enabled, the layout of the primary dex file will be generated using the startup list,
* using {@link com.android.tools.r8.dex.StartupMixedSectionLayoutStrategy}.
*/
private boolean enableStartupLayoutOptimizations =
parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.startup.layout", true);
- /**
- * Specifies the synthetic context of the startup runtime library. When this is set, the startup
- * runtime library will only be injected into the app when the synthetic context is in the
- * program. This can be used to avoid that the startup runtime library is injected multiple times
- * in presence of separate compilation.
- *
- * <p>Example synthetic context: "app.tivi.home.MainActivity".
- *
- * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
- */
- private String startupInstrumentationServerSyntheticContext =
- getSystemPropertyForDevelopment(
- "com.android.tools.r8.startup.instrumentationserversyntheticcontext");
-
- /**
- * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
- *
- * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
- * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
- * write the events to a file.
- */
- private String startupInstrumentationTag =
- getSystemPropertyForDevelopment("com.android.tools.r8.startup.instrumentationtag");
-
private StartupConfiguration startupConfiguration;
- public boolean hasStartupInstrumentationServerSyntheticContext() {
- return startupInstrumentationServerSyntheticContext != null;
- }
-
- public String getStartupInstrumentationServerSyntheticContext() {
- return startupInstrumentationServerSyntheticContext;
- }
-
- public StartupOptions setStartupInstrumentationServerSyntheticContext(
- String startupInstrumentationServerSyntheticContext) {
- this.startupInstrumentationServerSyntheticContext =
- startupInstrumentationServerSyntheticContext;
- return this;
- }
-
- public boolean hasStartupInstrumentationTag() {
- return startupInstrumentationTag != null;
- }
-
- public String getStartupInstrumentationTag() {
- return startupInstrumentationTag;
- }
-
- public StartupOptions setStartupInstrumentationTag(String startupInstrumentationTag) {
- this.startupInstrumentationTag = startupInstrumentationTag;
- return this;
- }
-
public boolean isMinimalStartupDexEnabled() {
return enableMinimalStartupDex;
}
@@ -128,15 +65,6 @@
return this;
}
- public boolean isStartupInstrumentationEnabled() {
- return enableStartupInstrumentation;
- }
-
- public StartupOptions setEnableStartupInstrumentation() {
- enableStartupInstrumentation = true;
- return this;
- }
-
public boolean isStartupLayoutOptimizationsEnabled() {
return enableStartupLayoutOptimizations;
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
similarity index 88%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index 2dfa4f2..7e8db58 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -2,7 +2,7 @@
// 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.utils.PredicateUtils.not;
@@ -54,21 +54,21 @@
private final IRConverter converter;
private final DexItemFactory dexItemFactory;
private final InternalOptions options;
- private final StartupReferences references;
- private final StartupOptions startupOptions;
+ private final StartupInstrumentationReferences references;
+ private final StartupInstrumentationOptions startupInstrumentationOptions;
private StartupInstrumentation(AppView<AppInfo> appView) {
this.appView = appView;
this.converter = new IRConverter(appView, Timing.empty());
this.dexItemFactory = appView.dexItemFactory();
this.options = appView.options();
- this.references = new StartupReferences(dexItemFactory);
- this.startupOptions = options.getStartupOptions();
+ this.references = new StartupInstrumentationReferences(dexItemFactory);
+ this.startupInstrumentationOptions = options.getStartupInstrumentationOptions();
}
public static void run(AppView<AppInfo> appView, ExecutorService executorService)
throws ExecutionException {
- if (appView.options().getStartupOptions().isStartupInstrumentationEnabled()) {
+ if (appView.options().getStartupInstrumentationOptions().isStartupInstrumentationEnabled()) {
StartupInstrumentation startupInstrumentation = new StartupInstrumentation(appView);
startupInstrumentation.instrumentAllClasses(executorService);
startupInstrumentation.injectStartupRuntimeLibrary(executorService);
@@ -89,11 +89,11 @@
// If the startup options has a synthetic context for the startup instrumentation server, then
// only inject the runtime library if the synthetic context exists in program to avoid injecting
// the runtime library multiple times when there is separate compilation.
- if (startupOptions.hasStartupInstrumentationServerSyntheticContext()) {
+ if (startupInstrumentationOptions.hasStartupInstrumentationServerSyntheticContext()) {
DexType syntheticContext =
dexItemFactory.createType(
DescriptorUtils.javaTypeToDescriptor(
- startupOptions.getStartupInstrumentationServerSyntheticContext()));
+ startupInstrumentationOptions.getStartupInstrumentationServerSyntheticContext()));
if (asProgramClassOrNull(appView.definitionFor(syntheticContext)) == null) {
return;
}
@@ -113,7 +113,7 @@
private List<DexProgramClass> createStartupRuntimeLibraryClasses() {
DexProgramClass instrumentationServerImplClass =
InstrumentationServerImplFactory.createClass(dexItemFactory);
- if (startupOptions.hasStartupInstrumentationTag()) {
+ if (startupInstrumentationOptions.hasStartupInstrumentationTag()) {
instrumentationServerImplClass
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("writeToLogcat"))
.setStaticValue(DexValueBoolean.create(true));
@@ -121,7 +121,8 @@
.lookupUniqueStaticFieldWithName(dexItemFactory.createString("logcatTag"))
.setStaticValue(
new DexValueString(
- dexItemFactory.createString(startupOptions.getStartupInstrumentationTag())));
+ dexItemFactory.createString(
+ startupInstrumentationOptions.getStartupInstrumentationTag())));
}
return ImmutableList.of(
@@ -174,8 +175,11 @@
// Insert invoke to record that the enclosing class is a startup class.
SyntheticItems syntheticItems = appView.getSyntheticItems();
- boolean isSyntheticClass = syntheticItems.isSyntheticClass(method.getHolder());
- if (method.getDefinition().isClassInitializer() && !isSyntheticClass) {
+ boolean generalizeSyntheticToSyntheticOfSyntheticContexts =
+ startupInstrumentationOptions.isGeneralizationOfSyntheticsToSyntheticContextEnabled()
+ && syntheticItems.isSyntheticClass(method.getHolder());
+ if (method.getDefinition().isClassInitializer()
+ && !generalizeSyntheticToSyntheticOfSyntheticContexts) {
DexMethod methodToInvoke = references.addNonSyntheticMethod;
DexType classToPrint = method.getHolderType();
Value descriptorValue =
@@ -193,7 +197,7 @@
if (!skipMethodLogging) {
DexMethod methodToInvoke;
DexReference referenceToPrint;
- if (isSyntheticClass) {
+ if (generalizeSyntheticToSyntheticOfSyntheticContexts) {
Collection<DexType> synthesizingContexts =
syntheticItems.getSynthesizingContextTypes(method.getHolderType());
assert synthesizingContexts.size() == 1;
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
new file mode 100644
index 0000000..bfb3f87
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationOptions.java
@@ -0,0 +1,104 @@
+// 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.experimental.startup.instrumentation;
+
+import static com.android.tools.r8.utils.SystemPropertyUtils.getSystemPropertyForDevelopment;
+import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyForDevelopmentOrDefault;
+
+public class StartupInstrumentationOptions {
+
+ /**
+ * When enabled, the instrumentation for synthetics will print the name of the synthetic context
+ * instead of printing the name of the synthetic itself.
+ */
+ private boolean enableGeneralizationOfSyntheticsToSyntheticContext =
+ parseSystemPropertyForDevelopmentOrDefault(
+ "com.android.tools.r8.startup.instrumentation.generalizesynthetics", false);
+
+ /**
+ * When enabled, each method will be instrumented to notify the startup InstrumentationServer that
+ * it has been executed.
+ *
+ * <p>This will also inject the startup runtime library (i.e., the InstrumentationServer) into the
+ * app.
+ */
+ private boolean enableStartupInstrumentation =
+ parseSystemPropertyForDevelopmentOrDefault(
+ "com.android.tools.r8.startup.instrumentation.instrument", false);
+
+ /**
+ * Specifies the synthetic context of the startup runtime library. When this is set, the startup
+ * runtime library will only be injected into the app when the synthetic context is in the
+ * program. This can be used to avoid that the startup runtime library is injected multiple times
+ * in presence of separate compilation.
+ *
+ * <p>Example synthetic context: "app.tivi.home.MainActivity".
+ *
+ * <p>Note that this is only meaningful when {@link #enableStartupInstrumentation} is set to true.
+ */
+ private String startupInstrumentationServerSyntheticContext =
+ getSystemPropertyForDevelopment(
+ "com.android.tools.r8.startup.instrumentation.instrumentationserversyntheticcontext");
+
+ /**
+ * Specifies the logcat tag that should be used by the InstrumentationServer when logging events.
+ *
+ * <p>When a logcat tag is not specified, the InstrumentationServer will not print events to
+ * logcat. Instead, the startup events must be obtained by requesting the InstrumentationServer to
+ * write the events to a file.
+ */
+ private String startupInstrumentationTag =
+ getSystemPropertyForDevelopment(
+ "com.android.tools.r8.startup.instrumentation.instrumentationtag");
+
+ public boolean hasStartupInstrumentationServerSyntheticContext() {
+ return startupInstrumentationServerSyntheticContext != null;
+ }
+
+ public String getStartupInstrumentationServerSyntheticContext() {
+ return startupInstrumentationServerSyntheticContext;
+ }
+
+ public StartupInstrumentationOptions setStartupInstrumentationServerSyntheticContext(
+ String startupInstrumentationServerSyntheticContext) {
+ this.startupInstrumentationServerSyntheticContext =
+ startupInstrumentationServerSyntheticContext;
+ return this;
+ }
+
+ public boolean hasStartupInstrumentationTag() {
+ return startupInstrumentationTag != null;
+ }
+
+ public String getStartupInstrumentationTag() {
+ return startupInstrumentationTag;
+ }
+
+ public StartupInstrumentationOptions setStartupInstrumentationTag(
+ String startupInstrumentationTag) {
+ this.startupInstrumentationTag = startupInstrumentationTag;
+ return this;
+ }
+
+ public boolean isGeneralizationOfSyntheticsToSyntheticContextEnabled() {
+ return enableGeneralizationOfSyntheticsToSyntheticContext;
+ }
+
+ public StartupInstrumentationOptions setEnableGeneralizationOfSyntheticsToSyntheticContext(
+ boolean enableGeneralizationOfSyntheticsToSyntheticContext) {
+ this.enableGeneralizationOfSyntheticsToSyntheticContext =
+ enableGeneralizationOfSyntheticsToSyntheticContext;
+ return this;
+ }
+
+ public boolean isStartupInstrumentationEnabled() {
+ return enableStartupInstrumentation;
+ }
+
+ public StartupInstrumentationOptions setEnableStartupInstrumentation() {
+ enableStartupInstrumentation = true;
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
similarity index 87%
rename from src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
rename to src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
index a041f1e..fbfa5e2 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupReferences.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentationReferences.java
@@ -2,20 +2,20 @@
// 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.experimental.startup;
+package com.android.tools.r8.experimental.startup.instrumentation;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-public class StartupReferences {
+class StartupInstrumentationReferences {
final DexType instrumentationServerType;
final DexType instrumentationServerImplType;
final DexMethod addNonSyntheticMethod;
final DexMethod addSyntheticMethod;
- StartupReferences(DexItemFactory dexItemFactory) {
+ StartupInstrumentationReferences(DexItemFactory dexItemFactory) {
instrumentationServerType =
dexItemFactory.createType("Lcom/android/tools/r8/startup/InstrumentationServer;");
instrumentationServerImplType =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 6065527..0d767c6 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -676,7 +676,11 @@
// actually equal.
GraphLens graphLens = appView.graphLens();
boolean includeContext =
- intermediate || appView.options().getStartupOptions().isStartupInstrumentationEnabled();
+ intermediate
+ || appView
+ .options()
+ .getStartupInstrumentationOptions()
+ .isStartupInstrumentationEnabled();
List<T> sortedPotentialMembers =
ListUtils.sort(
potentialEquivalence,
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 065369b..d784613 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
import com.android.tools.r8.features.FeatureSplitConfiguration;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -816,6 +817,8 @@
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
private final StartupOptions startupOptions = new StartupOptions();
+ private final StartupInstrumentationOptions startupInstrumentationOptions =
+ new StartupInstrumentationOptions();
public final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -879,6 +882,10 @@
return startupOptions;
}
+ public StartupInstrumentationOptions getStartupInstrumentationOptions() {
+ return startupInstrumentationOptions;
+ }
+
public TestingOptions getTestingOptions() {
return testing;
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index f343c0f..be01cef 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -112,18 +112,10 @@
}
@SafeVarargs
+ @Override
public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
- Path out = state.getNewTempFolder();
- getApp().writeToDirectory(out, OutputMode.DexIndexed);
- consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), getProguardMap()));
- for (int i = 1; i < consumers.length; i++) {
- Path dex = out.resolve("classes" + (i + 1) + ".dex");
- CodeInspector inspector =
- dex.toFile().exists() ? new CodeInspector(dex, getProguardMap()) : CodeInspector.empty();
- consumers[i].accept(inspector);
- }
- return self();
+ return inspectMultiDex(writeProguardMap(), consumers);
}
public final <E extends Throwable> R8TestCompileResult inspectGraph(
diff --git a/src/test/java/com/android/tools/r8/TestCompileResult.java b/src/test/java/com/android/tools/r8/TestCompileResult.java
index 4d557ea..b15e669 100644
--- a/src/test/java/com/android/tools/r8/TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/TestCompileResult.java
@@ -444,6 +444,27 @@
return self();
}
+ @SuppressWarnings("unchecked")
+ public <E extends Throwable> CR inspectMultiDex(ThrowingConsumer<CodeInspector, E>... consumers)
+ throws IOException, E {
+ return inspectMultiDex(null, consumers);
+ }
+
+ @SafeVarargs
+ public final <E extends Throwable> CR inspectMultiDex(
+ Path mappingFile, ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
+ Path out = state.getNewTempFolder();
+ getApp().writeToDirectory(out, OutputMode.DexIndexed);
+ consumers[0].accept(new CodeInspector(out.resolve("classes.dex"), mappingFile));
+ for (int i = 1; i < consumers.length; i++) {
+ Path dex = out.resolve("classes" + (i + 1) + ".dex");
+ CodeInspector inspector =
+ dex.toFile().exists() ? new CodeInspector(dex, mappingFile) : CodeInspector.empty();
+ consumers[i].accept(inspector);
+ }
+ return self();
+ }
+
public <E extends Throwable> CR inspectWithOptions(
ThrowingConsumer<CodeInspector, E> consumer, Consumer<InternalOptions> debugOptionsConsumer)
throws IOException, E {
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
index 0b404a6..c460799 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1719Test.java
@@ -90,7 +90,7 @@
.addOptionsModification(
options ->
options
- .getStartupOptions()
+ .getStartupInstrumentationOptions()
.setEnableStartupInstrumentation()
.setStartupInstrumentationTag("r8"))
.enableCoreLibraryDesugaring(
diff --git a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
index 390e702..77f33b2 100644
--- a/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
+++ b/src/test/java/com/android/tools/r8/internal/startup/ChromeStartupTest.java
@@ -78,7 +78,7 @@
.addOptionsModification(
options ->
options
- .getStartupOptions()
+ .getStartupInstrumentationOptions()
.setEnableStartupInstrumentation()
.setStartupInstrumentationTag("r8"))
.setMinApi(apiLevel)
@@ -98,7 +98,7 @@
.addOptionsModification(
options ->
options
- .getStartupOptions()
+ .getStartupInstrumentationOptions()
.setEnableStartupInstrumentation()
.setStartupInstrumentationTag("r8"))
.setMinApi(apiLevel)
diff --git a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
index a433265..35df0b6 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.startup.StartupSyntheticPlacementTest.Main;
import com.android.tools.r8.startup.utils.StartupTestingUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -49,7 +48,8 @@
List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
- .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .apply(
+ StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
.release()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
index 07f4487..bc0292f 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupInstrumentationTest.java
@@ -53,8 +53,8 @@
.addInnerClasses(getClass())
.applyIf(
logcat,
- StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters),
- StartupTestingUtils.enableStartupInstrumentationUsingFile(parameters))
+ StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters),
+ StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingFile(parameters))
.release()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
index 0cce865..e89bee0 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -11,12 +11,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupItem;
import com.android.tools.r8.experimental.startup.StartupMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
@@ -26,8 +30,11 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -64,35 +71,89 @@
}
@Test
- public void test() throws Exception {
+ public void testLayoutUsingD8() throws Exception {
+ // First build the app using R8.
+ R8TestCompileResult r8CompileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(A.class, B.class, C.class)
+ .addDontOptimize()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ // Verify that the build works.
+ r8CompileResult
+ .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+ .assertSuccessWithOutputLines(getExpectedOutput());
+
+ Path optimizedApp = r8CompileResult.writeToZip();
+
+ // Then instrument the app to generate a startup list for the minified app.
List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
- .addInnerClasses(getClass())
- .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .addProgramFiles(optimizedApp)
+ .apply(
+ StartupTestingUtils.enableStartupInstrumentationForOptimizedAppUsingLogcat(parameters))
.release()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
.run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
.apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
- .assertSuccessWithOutputLines(getExpectedOutput());
- assertEquals(getExpectedStartupList(), startupList);
+ .assertSuccessWithOutputLines(getExpectedOutput())
+ .apply(
+ runResult ->
+ assertEquals(
+ getExpectedStartupList(r8CompileResult.inspector(), false), startupList));
+ // Finally rebuild the minified app using D8 and the startup list.
+ testForD8(parameters.getBackend())
+ .addProgramFiles(optimizedApp)
+ .apply(
+ testBuilder ->
+ configureStartupOptions(testBuilder, r8CompileResult.inspector(), startupList))
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectMultiDex(
+ r8CompileResult.writeProguardMap(), this::inspectPrimaryDex, this::inspectSecondaryDex)
+ .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ @Test
+ public void testLayoutUsingR8() throws Exception {
+ // First generate a startup list for the original app.
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ D8TestCompileResult instrumentationCompileResult =
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .apply(
+ StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(
+ parameters))
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ instrumentationCompileResult
+ .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
+ .run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
+ .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .assertSuccessWithOutputLines(getExpectedOutput())
+ .apply(
+ runResult ->
+ assertEquals(getExpectedStartupList(runResult.inspector(), true), startupList));
+
+ // Then build the app using the startup list that is based on original names.
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addKeepClassAndMembersRules(A.class, B.class, C.class)
- .addOptionsModification(
- options -> {
- options
- .getStartupOptions()
- .setEnableMinimalStartupDex(enableMinimalStartupDex)
- .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
- options
- .getTestingOptions()
- .setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
- })
- .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
+ .apply(
+ testBuilder ->
+ configureStartupOptions(
+ testBuilder, instrumentationCompileResult.inspector(), startupList))
.setMinApi(parameters.getApiLevel())
.compile()
.inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
@@ -103,12 +164,31 @@
runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
}
+ private void configureStartupOptions(
+ TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
+ CodeInspector inspector,
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList) {
+ testBuilder
+ .addOptionsModification(
+ options -> {
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex(enableMinimalStartupDex)
+ .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
+ options
+ .getTestingOptions()
+ .setMixedSectionLayoutStrategyInspector(
+ getMixedSectionLayoutInspector(inspector));
+ })
+ .apply(ignore -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList));
+ }
+
private List<String> getExpectedOutput() {
return ImmutableList.of("A", "B", "C");
}
- private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList()
- throws NoSuchMethodException {
+ private List<StartupItem<ClassReference, MethodReference, ?>> getExpectedStartupList(
+ CodeInspector inspector, boolean isStartupListForOriginalApp) throws NoSuchMethodException {
ImmutableList.Builder<StartupItem<ClassReference, MethodReference, ?>> builder =
ImmutableList.builder();
builder.add(
@@ -132,11 +212,56 @@
Reference.methodFromMethod(B.class.getDeclaredMethod("b", boolean.class)))
.build());
if (useLambda) {
+ if (isStartupListForOriginalApp) {
+ builder.add(
+ StartupClass.referenceBuilder()
+ .setClassReference(Reference.classFromClass(B.class))
+ .setSynthetic()
+ .build());
+ } else {
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+
+ MethodSubject syntheticLambdaAccessorMethod =
+ bClassSubject.uniqueMethodThatMatches(
+ method ->
+ method
+ .getOriginalName()
+ .startsWith(LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX));
+ assertThat(syntheticLambdaAccessorMethod, isPresent());
+
+ ClassSubject externalSyntheticLambdaClassSubject =
+ inspector.clazz(getSyntheticLambdaClassReference());
+ assertThat(externalSyntheticLambdaClassSubject, isPresent());
+
+ ClassReference externalSyntheticLambdaClassReference =
+ externalSyntheticLambdaClassSubject.getFinalReference();
+
+ builder.add(
+ StartupClass.referenceBuilder()
+ .setClassReference(externalSyntheticLambdaClassReference)
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ MethodReferenceUtils.instanceConstructor(externalSyntheticLambdaClassReference))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.method(
+ externalSyntheticLambdaClassReference,
+ "accept",
+ ImmutableList.of(Reference.classFromClass(Object.class)),
+ null))
+ .build(),
+ StartupMethod.referenceBuilder()
+ .setMethodReference(
+ Reference.method(
+ Reference.classFromClass(B.class),
+ syntheticLambdaAccessorMethod.getFinalName(),
+ ImmutableList.of(Reference.classFromClass(Object.class)),
+ null))
+ .build());
+ }
builder.add(
- StartupClass.referenceBuilder()
- .setClassReference(Reference.classFromClass(B.class))
- .setSynthetic()
- .build(),
StartupMethod.referenceBuilder()
.setMethodReference(
Reference.methodFromMethod(B.class.getDeclaredMethod("lambda$b$0", Object.class)))
@@ -152,7 +277,10 @@
return builder.build();
}
- private List<ClassReference> getExpectedClassDataLayout(int virtualFile) {
+ private List<ClassReference> getExpectedClassDataLayout(
+ CodeInspector inspector, int virtualFile) {
+ ClassSubject syntheticLambdaClassSubject = inspector.clazz(getSyntheticLambdaClassReference());
+
// The synthetic lambda should only be placed alongside its synthetic context (B) if it is used.
// Otherwise, it should be last, or in the second dex file if compiling with minimal startup.
ImmutableList.Builder<ClassReference> layoutBuilder = ImmutableList.builder();
@@ -162,23 +290,24 @@
Reference.classFromClass(A.class),
Reference.classFromClass(B.class));
if (useLambda) {
- layoutBuilder.add(getSyntheticLambdaClassReference());
+ layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
}
layoutBuilder.add(Reference.classFromClass(C.class));
}
if (!useLambda) {
if (!enableMinimalStartupDex || virtualFile == 1) {
- layoutBuilder.add(getSyntheticLambdaClassReference());
+ layoutBuilder.add(syntheticLambdaClassSubject.getFinalReference());
}
}
return layoutBuilder.build();
}
- private MixedSectionLayoutInspector getMixedSectionLayoutInspector() {
+ private MixedSectionLayoutInspector getMixedSectionLayoutInspector(CodeInspector inspector) {
return new MixedSectionLayoutInspector() {
@Override
public void inspectClassDataLayout(int virtualFile, Collection<DexProgramClass> layout) {
- assertThat(layout, isEqualToClassDataLayout(getExpectedClassDataLayout(virtualFile)));
+ assertThat(
+ layout, isEqualToClassDataLayout(getExpectedClassDataLayout(inspector, virtualFile)));
}
};
}
diff --git a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
index 13c7ca7..b9ceea2 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -64,7 +64,8 @@
List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
testForD8(parameters.getBackend())
.addInnerClasses(getClass())
- .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .apply(
+ StartupTestingUtils.enableStartupInstrumentationForOriginalAppUsingLogcat(parameters))
.release()
.setMinApi(parameters.getApiLevel())
.compile()
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index ae31fe8..04d673c 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -10,14 +10,14 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestRunResult;
-import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
import com.android.tools.r8.experimental.startup.StartupItem;
-import com.android.tools.r8.experimental.startup.StartupOptions;
+import com.android.tools.r8.experimental.startup.instrumentation.StartupInstrumentationOptions;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
@@ -40,25 +40,54 @@
private static String startupInstrumentationTag = "startup";
- public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingFile(
- TestParameters parameters) {
- return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, false);
+ private enum AppVariant {
+ ORIGINAL,
+ OPTIMIZED;
+
+ boolean isOriginal() {
+ return this == ORIGINAL;
+ }
}
- public static ThrowableConsumer<D8TestBuilder> enableStartupInstrumentationUsingLogcat(
- TestParameters parameters) {
- return testBuilder -> enableStartupInstrumentation(testBuilder, parameters, true);
+ public static ThrowableConsumer<D8TestBuilder>
+ enableStartupInstrumentationForOriginalAppUsingFile(TestParameters parameters) {
+ return testBuilder ->
+ enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, false);
+ }
+
+ public static ThrowableConsumer<D8TestBuilder>
+ enableStartupInstrumentationForOriginalAppUsingLogcat(TestParameters parameters) {
+ return testBuilder ->
+ enableStartupInstrumentation(testBuilder, parameters, AppVariant.ORIGINAL, true);
+ }
+
+ public static ThrowableConsumer<D8TestBuilder>
+ enableStartupInstrumentationForOptimizedAppUsingFile(TestParameters parameters) {
+ return testBuilder ->
+ enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, false);
+ }
+
+ public static ThrowableConsumer<D8TestBuilder>
+ enableStartupInstrumentationForOptimizedAppUsingLogcat(TestParameters parameters) {
+ return testBuilder ->
+ enableStartupInstrumentation(testBuilder, parameters, AppVariant.OPTIMIZED, true);
}
private static void enableStartupInstrumentation(
- D8TestBuilder testBuilder, TestParameters parameters, boolean logcat) throws IOException {
+ D8TestBuilder testBuilder, TestParameters parameters, AppVariant appVariant, boolean logcat)
+ throws IOException {
testBuilder
.addOptionsModification(
options -> {
- StartupOptions startupOptions =
- options.getStartupOptions().setEnableStartupInstrumentation();
+ StartupInstrumentationOptions startupInstrumentationOptions =
+ options
+ .getStartupInstrumentationOptions()
+ .setEnableStartupInstrumentation()
+ .setEnableGeneralizationOfSyntheticsToSyntheticContext(
+ appVariant.isOriginal());
if (logcat) {
- startupOptions.setStartupInstrumentationTag(startupInstrumentationTag);
+ startupInstrumentationOptions.setStartupInstrumentationTag(
+ startupInstrumentationTag);
}
})
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
@@ -113,7 +142,7 @@
}
public static void setStartupConfiguration(
- R8TestBuilder<?> testBuilder,
+ TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder,
List<StartupItem<ClassReference, MethodReference, ?>> startupItems) {
testBuilder.addOptionsModification(
options -> {