Merge commit 'e069b00496e7dab73907ae88ad55390072ba6e4b' into dev-release
diff --git a/src/library_desugar/jdk11/desugar_jdk_libs_path.json b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
index a00bb68..e1298de 100644
--- a/src/library_desugar/jdk11/desugar_jdk_libs_path.json
+++ b/src/library_desugar/jdk11/desugar_jdk_libs_path.json
@@ -309,16 +309,6 @@
}
},
{
- "api_level_below_or_equal": 25,
- "rewrite_prefix": {
- "java.io.DesugarFile": "j$.io.DesugarFile",
- "java.nio.file.": "j$.nio.file."
- },
- "retarget_method_with_emulated_dispatch": {
- "java.nio.file.Path java.io.File#toPath()": "java.io.DesugarFile"
- }
- },
- {
"api_level_below_or_equal": 23,
"retarget_method": {
"int java.util.concurrent.atomic.AtomicInteger#accumulateAndGet(int, java.util.function.IntBinaryOperator)": "java.util.concurrent.atomic.DesugarAtomicInteger",
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index a94d834..b46e8f5 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -47,6 +47,9 @@
throws CompilationFailedException {
InternalOptions options = command.getInternalOptions();
+ // TODO(b/241351268): Don't compile in intermediate mode as the output is a final "shard".
+ options.intermediate = true;
+
// TODO(b/241063980): Move this to D8Command.Builder.setDisableDesugaring(true) in bazel.
options.desugarState = DesugarState.OFF;
@@ -62,11 +65,4 @@
D8.runForTesting(command.getInputApp(), options);
}
-
- public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
- throws CompilationFailedException {
- InternalOptions options = command.getInternalOptions();
- options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
- D8.runForTesting(command.getInputApp(), options);
- }
}
diff --git a/src/main/java/com/android/tools/r8/DexSegments.java b/src/main/java/com/android/tools/r8/DexSegments.java
index c9073be..b009e9d 100644
--- a/src/main/java/com/android/tools/r8/DexSegments.java
+++ b/src/main/java/com/android/tools/r8/DexSegments.java
@@ -22,21 +22,30 @@
public class DexSegments {
public static class Command extends BaseCommand {
+ private final boolean csv;
+
public static class Builder
extends BaseCommand.Builder<Command, Builder> {
+ private boolean csv = false;
+
@Override
Command.Builder self() {
return this;
}
+ private Builder setCsv(boolean csv) {
+ this.csv = csv;
+ return self();
+ }
+
@Override
protected Command makeCommand() {
// If printing versions ignore everything else.
if (isPrintHelp()) {
return new Command(isPrintHelp());
}
- return new Command(getAppBuilder().build());
+ return new Command(getAppBuilder().build(), csv);
}
}
@@ -63,6 +72,8 @@
continue;
} else if (arg.equals("--help")) {
builder.setPrintHelp(true);
+ } else if (arg.equals("--csv")) {
+ builder.setCsv(true);
} else {
if (arg.startsWith("--")) {
builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg,
@@ -73,12 +84,14 @@
}
}
- private Command(AndroidApp inputApp) {
+ private Command(AndroidApp inputApp, boolean csv) {
super(inputApp);
+ this.csv = csv;
}
private Command(boolean printHelp) {
super(printHelp, false);
+ this.csv = false;
}
@Override
@@ -90,16 +103,41 @@
public static void main(String[] args)
throws IOException, CompilationFailedException, ResourceException {
Command.Builder builder = Command.parse(args);
- Map<Integer, SegmentInfo> result = run(builder.build());
+ Command cmd = builder.build();
+ Map<Integer, SegmentInfo> result = run(cmd);
if (result == null) {
return;
}
- System.out.println("Segments in dex application (name: size / items):");
- // This output is parsed by tools/test_framework.py. Check the parsing there when updating.
- result.forEach(
- (key, value) ->
+ if (cmd.csv) {
+ System.out.println("\"Name\",\"Size\",\"Items\"");
+ result.forEach(
+ (key, value) -> {
System.out.println(
- " - " + DexSection.typeName(key) + ": " + value.size + " / " + value.items));
+ "\"" + DexSection.typeName(key) + "\", " + value.size + ", " + value.items);
+ if (key == Constants.TYPE_TYPE_LIST) {
+ // Type items header is just a uint, and each element is a ushort. see
+ // https://source.android.com/devices/tech/dalvik/dex-format#type-list.
+ int typeItemsSize = (value.size - value.items * 4);
+ System.out.println(
+ "\"TypeItems\", " + typeItemsSize + ", " + (typeItemsSize / 2) + "");
+ }
+ });
+ } else {
+ System.out.println("Segments in dex application (name: size / items):");
+ // This output is parsed by tools/test_framework.py. Check the parsing there when updating.
+ result.forEach(
+ (key, value) -> {
+ System.out.print(
+ " - " + DexSection.typeName(key) + ": " + value.size + " / " + value.items);
+ if (key == Constants.TYPE_TYPE_LIST) {
+ // Type items header is just a uint, and each element is a ushort. see
+ // https://source.android.com/devices/tech/dalvik/dex-format#type-list.
+ int typeItemsSize = (value.size - value.items * 4);
+ System.out.print(" (TypeItems: " + typeItemsSize + " / " + (typeItemsSize / 2) + ")");
+ }
+ System.out.println();
+ });
+ }
}
public static Map<Integer, SegmentInfo> run(Command command)
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index 351a3a1..b9ef649 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -284,6 +284,9 @@
}
private void freeRegistersForIntervals(LiveIntervals intervals) {
+ if (options().testing.neverReuseCfLocalRegisters) {
+ return;
+ }
int register = intervals.getRegister();
freeRegisters.add(register);
if (intervals.getType().isWide()) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 31967b1..a64b883 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -166,7 +166,7 @@
}
Path dumpOutput = dumpInputFlags.getDumpPath();
timing.begin("ApplicationReader.dump");
- inputApp.dump(dumpOutput, dumpOptions, options.reporter, options.dexItemFactory());
+ inputApp.dump(dumpOutput, dumpOptions, options);
timing.end();
Diagnostic message = new StringDiagnostic("Dumped compilation inputs to: " + dumpOutput);
if (dumpInputFlags.shouldFailCompilation()) {
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 492e61a..b0d4999 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.dex.FileWriter.ByteBufferResult;
import com.android.tools.r8.dex.VirtualFile.FilePerInputClassDistributor;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.experimental.startup.StartupCompleteness;
import com.android.tools.r8.features.FeatureSplitConfiguration.DataResourceProvidersAndConsumer;
import com.android.tools.r8.graph.AppServices;
import com.android.tools.r8.graph.AppView;
@@ -289,6 +290,8 @@
timing.end();
}
+ StartupCompleteness.run(appView);
+
// Generate the dex file contents.
timing.begin("Distribute");
List<VirtualFile> virtualFiles = distribute(executorService);
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 7143d6b..2381e6f 100644
--- a/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
+++ b/src/main/java/com/android/tools/r8/dex/MixedSectionLayoutStrategy.java
@@ -23,7 +23,9 @@
public static MixedSectionLayoutStrategy create(
AppView<?> appView, MixedSectionOffsets mixedSectionOffsets, VirtualFile virtualFile) {
StartupOrder startupOrderForWriting =
- virtualFile.getId() == 0 && appView.hasClassHierarchy()
+ appView.options().getStartupOptions().isStartupLayoutOptimizationsEnabled()
+ && virtualFile.getId() == 0
+ && appView.hasClassHierarchy()
? appView
.appInfoWithClassHierarchy()
.getStartupOrder()
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 1056bda..55923ed 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
@@ -27,9 +26,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
-import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
-import com.android.tools.r8.graph.ThrowNullCode;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
@@ -48,7 +45,6 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterators;
import com.google.common.collect.Sets;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.IOException;
@@ -1010,7 +1006,6 @@
public void run() {
addStartupClasses();
- enableStartupCompletenessCheckForTesting();
List<DexProgramClass> nonPackageClasses = addNonStartupClasses();
addNonPackageClasses(cycler, nonPackageClasses);
}
@@ -1057,31 +1052,6 @@
}
}
- /**
- * Replaces the code of each method of a non-startup class by {@code throw null}. If the
- * application fails on launch with this enabled this points to the startup configuration being
- * incomplete.
- */
- private void enableStartupCompletenessCheckForTesting() {
- if (!options.getStartupOptions().isStartupCompletenessCheckForTesting()) {
- return;
- }
- for (DexProgramClass clazz : classPartioning.getNonStartupClasses()) {
- clazz.forEachProgramMethodMatching(
- DexEncodedMethod::hasCode,
- method ->
- method.getDefinition().setCode(ThrowNullCode.get(), Int2ReferenceMaps.emptyMap()));
- if (!clazz.hasClassInitializer()) {
- clazz.addDirectMethod(
- DexEncodedMethod.syntheticBuilder()
- .setAccessFlags(MethodAccessFlags.createForClassInitializer())
- .setCode(ThrowNullCode.get())
- .setMethod(dexItemFactory.createClassInitializer(clazz.getType()))
- .build());
- }
- }
- }
-
private List<DexProgramClass> addNonStartupClasses() {
int prefixLength = MINIMUM_PREFIX_LENGTH;
int transactionStartIndex = 0;
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 4b749f3..954fcba 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
@@ -16,10 +16,8 @@
import com.android.tools.r8.utils.LazyBox;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -131,7 +129,7 @@
LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems =
new LinkedHashSet<>(startupItems.size());
Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
- computeSyntheticContextsToSyntheticClasses(appView);
+ appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
for (StartupItem<DexType, DexMethod, ?> startupItem : startupItems) {
addStartupItem(
startupItem, rewrittenStartupItems, syntheticContextsToSyntheticClasses, appView);
@@ -140,23 +138,6 @@
return createNonEmpty(rewrittenStartupItems);
}
- private Map<DexType, List<DexProgramClass>> computeSyntheticContextsToSyntheticClasses(
- AppView<?> appView) {
- 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);
- }
- }
- }
- return syntheticContextsToSyntheticClasses;
- }
-
private static void addStartupItem(
StartupItem<DexType, DexMethod, ?> startupItem,
LinkedHashSet<StartupItem<DexType, DexMethod, ?>> rewrittenStartupItems,
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
new file mode 100644
index 0000000..8d75d7c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupCompleteness.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;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ThrowNullCode;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class StartupCompleteness {
+
+ private final AppView<?> appView;
+ private final StartupOrder startupOrder;
+
+ private StartupCompleteness(AppView<?> appView) {
+ this.appView = appView;
+ this.startupOrder =
+ appView.hasClassHierarchy()
+ ? appView.appInfoWithClassHierarchy().getStartupOrder()
+ : StartupOrder.empty();
+ }
+
+ /**
+ * Replaces the code of each non-startup method by {@code throw null}. If the application fails on
+ * launch with this enabled this points to the startup configuration being incomplete, or
+ * inadequate lens rewriting of the startup list in R8.
+ */
+ public static void run(AppView<?> appView) {
+ InternalOptions options = appView.options();
+ if (options.getStartupOptions().isStartupCompletenessCheckForTestingEnabled()) {
+ new StartupCompleteness(appView).processClasses();
+ }
+ }
+
+ private void processClasses() {
+ Set<DexReference> startupItems = computeStartupItems();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ processClass(clazz, startupItems);
+ }
+ }
+
+ private void processClass(DexProgramClass clazz, Set<DexReference> startupItems) {
+ clazz.forEachProgramMethodMatching(
+ method -> !startupItems.contains(method.getReference()), this::processNonStartupMethod);
+ if (!startupItems.contains(clazz.getType())) {
+ if (clazz.hasClassInitializer()) {
+ processNonStartupMethod(clazz.getProgramClassInitializer());
+ } else {
+ clazz.addDirectMethod(
+ DexEncodedMethod.syntheticBuilder()
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setCode(ThrowNullCode.get())
+ .setMethod(appView.dexItemFactory().createClassInitializer(clazz.getType()))
+ .build());
+ }
+ }
+ }
+
+ private void processNonStartupMethod(ProgramMethod method) {
+ method.getDefinition().setCode(ThrowNullCode.get(), Int2ReferenceMaps.emptyMap());
+ }
+
+ private Set<DexReference> computeStartupItems() {
+ Set<DexReference> startupItems = Sets.newIdentityHashSet();
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
+ appView.getSyntheticItems().computeSyntheticContextsToSyntheticClasses(appView);
+ for (StartupItem<DexType, DexMethod, ?> startupItem : startupOrder.getItems()) {
+ if (startupItem.isSynthetic()) {
+ assert startupItem.isStartupClass();
+ List<DexProgramClass> syntheticClasses =
+ syntheticContextsToSyntheticClasses.getOrDefault(
+ startupItem.asStartupClass().getReference(), Collections.emptyList());
+ for (DexProgramClass syntheticClass : syntheticClasses) {
+ startupItems.add(syntheticClass.getType());
+ syntheticClass.forEachProgramMethod(method -> startupItems.add(method.getReference()));
+ }
+ } else {
+ if (startupItem.isStartupClass()) {
+ StartupClass<DexType, DexMethod> startupClass = startupItem.asStartupClass();
+ startupItems.add(startupClass.getReference());
+ } else {
+ assert startupItem.isStartupMethod();
+ StartupMethod<DexType, DexMethod> startupMethod = startupItem.asStartupMethod();
+ startupItems.add(startupMethod.getReference());
+ }
+ }
+ }
+ return startupItems;
+ }
+}
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 7ff913d..e737c9d 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
@@ -18,6 +18,16 @@
"com.android.tools.r8.startup.minimalstartupdex", false);
/**
+ * When enabled, optimizations crossing the startup/non-startup boundary will be allowed.
+ *
+ * <p>The disabling of this may help to avoid that more code may be loaded during startup as a
+ * result of optimizations such as inlining and class merging.
+ */
+ private boolean enableStartupBoundaryOptimizations =
+ parseSystemPropertyForDevelopmentOrDefault(
+ "com.android.tools.r8.startup.boundaryoptimizations", false);
+
+ /**
* When enabled, each method that is not classified as a startup method at the end of compilation
* will be changed to have a throwing method body.
*
@@ -39,6 +49,13 @@
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
@@ -101,6 +118,10 @@
return this;
}
+ public boolean isStartupBoundaryOptimizationsEnabled() {
+ return enableStartupBoundaryOptimizations;
+ }
+
public boolean isStartupInstrumentationEnabled() {
return enableStartupInstrumentation;
}
@@ -110,12 +131,21 @@
return this;
}
- public boolean isStartupCompletenessCheckForTesting() {
+ public boolean isStartupLayoutOptimizationsEnabled() {
+ return enableStartupLayoutOptimizations;
+ }
+
+ public boolean isStartupCompletenessCheckForTestingEnabled() {
return enableStartupCompletenessCheckForTesting;
}
public StartupOptions setEnableStartupCompletenessCheckForTesting() {
- enableStartupCompletenessCheckForTesting = true;
+ return setEnableStartupCompletenessCheckForTesting(true);
+ }
+
+ public StartupOptions setEnableStartupCompletenessCheckForTesting(
+ boolean enableStartupCompletenessCheckForTesting) {
+ this.enableStartupCompletenessCheckForTesting = enableStartupCompletenessCheckForTesting;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
index 2113b52..5b8e3db 100644
--- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
+++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -107,14 +107,20 @@
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
Set<DexProgramClass> classes, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getFeatureSplitClasses(
- classes, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ classes,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public Map<FeatureSplit, Set<DexProgramClass>> getFeatureSplitClasses(
- Set<DexProgramClass> classes, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ Set<DexProgramClass> classes,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
Map<FeatureSplit, Set<DexProgramClass>> result = new IdentityHashMap<>();
for (DexProgramClass clazz : classes) {
- FeatureSplit featureSplit = getFeatureSplit(clazz, startupOrder, syntheticItems);
+ FeatureSplit featureSplit = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
if (featureSplit != null && !featureSplit.isBase()) {
result.computeIfAbsent(featureSplit, ignore -> Sets.newIdentityHashSet()).add(clazz);
}
@@ -125,21 +131,31 @@
public FeatureSplit getFeatureSplit(
ProgramDefinition definition, AppView<? extends AppInfoWithClassHierarchy> appView) {
return getFeatureSplit(
- definition, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ definition,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public FeatureSplit getFeatureSplit(
- ProgramDefinition definition, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return getFeatureSplit(definition.getContextType(), startupOrder, syntheticItems);
+ ProgramDefinition definition,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return getFeatureSplit(definition.getContextType(), options, startupOrder, syntheticItems);
}
public FeatureSplit getFeatureSplit(
DexType type, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return getFeatureSplit(type, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ return getFeatureSplit(
+ type, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public FeatureSplit getFeatureSplit(
- DexType type, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ DexType type,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
if (syntheticItems == null) {
// Called from AndroidApp.dumpProgramResources().
assert startupOrder.isEmpty();
@@ -154,16 +170,20 @@
// the shared utility class in case it is used during startup. The use of base startup
// allows for merging startup classes with the shared utility class, however, which could be
// bad for startup if the shared utility class is not used during startup.
- return startupOrder.isEmpty() ? FeatureSplit.BASE : FeatureSplit.BASE_STARTUP;
+ return startupOrder.isEmpty()
+ || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
+ ? FeatureSplit.BASE
+ : FeatureSplit.BASE_STARTUP;
}
feature = syntheticItems.getContextualFeatureSplitOrDefault(type, FeatureSplit.BASE);
} else {
feature = classToFeatureSplitMap.getOrDefault(type, FeatureSplit.BASE);
}
if (feature.isBase()) {
- return startupOrder.contains(type, syntheticItems)
- ? FeatureSplit.BASE_STARTUP
- : FeatureSplit.BASE;
+ return !startupOrder.contains(type, syntheticItems)
+ || options.getStartupOptions().isStartupBoundaryOptimizationsEnabled()
+ ? FeatureSplit.BASE
+ : FeatureSplit.BASE_STARTUP;
}
return feature;
}
@@ -177,12 +197,16 @@
public boolean isInBase(
DexProgramClass clazz, AppView<? extends AppInfoWithClassHierarchy> appView) {
- return isInBase(clazz, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ return isInBase(
+ clazz, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInBase(
- DexProgramClass clazz, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return getFeatureSplit(clazz, startupOrder, syntheticItems).isBase();
+ DexProgramClass clazz,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return getFeatureSplit(clazz, options, startupOrder, syntheticItems).isBase();
}
public boolean isInBaseOrSameFeatureAs(
@@ -190,15 +214,21 @@
ProgramDefinition context,
AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInBaseOrSameFeatureAs(
- clazz, context, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ clazz,
+ context,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public boolean isInBaseOrSameFeatureAs(
DexProgramClass clazz,
ProgramDefinition context,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- return isInBaseOrSameFeatureAs(clazz.getContextType(), context, startupOrder, syntheticItems);
+ return isInBaseOrSameFeatureAs(
+ clazz.getContextType(), context, options, startupOrder, syntheticItems);
}
public boolean isInBaseOrSameFeatureAs(
@@ -206,48 +236,62 @@
ProgramDefinition context,
AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInBaseOrSameFeatureAs(
- clazz, context, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ clazz,
+ context,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
public boolean isInBaseOrSameFeatureAs(
DexType clazz,
ProgramDefinition context,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- FeatureSplit split = getFeatureSplit(clazz, startupOrder, syntheticItems);
- return split.isBase() || split == getFeatureSplit(context, startupOrder, syntheticItems);
+ FeatureSplit split = getFeatureSplit(clazz, options, startupOrder, syntheticItems);
+ return split.isBase()
+ || split == getFeatureSplit(context, options, startupOrder, syntheticItems);
}
public boolean isInFeature(
- DexProgramClass clazz, StartupOrder startupOrder, SyntheticItems syntheticItems) {
- return !isInBase(clazz, startupOrder, syntheticItems);
+ DexProgramClass clazz,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
+ return !isInBase(clazz, options, startupOrder, syntheticItems);
}
public boolean isInSameFeatureOrBothInSameBase(
ProgramMethod a, ProgramMethod b, AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInSameFeatureOrBothInSameBase(
- a, b, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInSameFeatureOrBothInSameBase(
- ProgramMethod a, ProgramMethod b, StartupOrder startupOrder, SyntheticItems syntheticItems) {
+ ProgramMethod a,
+ ProgramMethod b,
+ InternalOptions options,
+ StartupOrder startupOrder,
+ SyntheticItems syntheticItems) {
return isInSameFeatureOrBothInSameBase(
- a.getHolder(), b.getHolder(), startupOrder, syntheticItems);
+ a.getHolder(), b.getHolder(), options, startupOrder, syntheticItems);
}
public boolean isInSameFeatureOrBothInSameBase(
DexProgramClass a, DexProgramClass b, AppView<? extends AppInfoWithClassHierarchy> appView) {
return isInSameFeatureOrBothInSameBase(
- a, b, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ a, b, appView.options(), appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
}
public boolean isInSameFeatureOrBothInSameBase(
DexProgramClass a,
DexProgramClass b,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
- return getFeatureSplit(a, startupOrder, syntheticItems)
- == getFeatureSplit(b, startupOrder, syntheticItems);
+ return getFeatureSplit(a, options, startupOrder, syntheticItems)
+ == getFeatureSplit(b, options, startupOrder, syntheticItems);
}
public ClassToFeatureSplitMap rewrittenWithLens(GraphLens lens) {
@@ -287,6 +331,10 @@
public static boolean isInFeature(DexProgramClass clazz, AppView<AppInfoWithLiveness> appView) {
return getMap(appView)
- .isInFeature(clazz, appView.appInfo().getStartupOrder(), appView.getSyntheticItems());
+ .isInFeature(
+ clazz,
+ appView.options(),
+ appView.appInfo().getStartupOrder(),
+ appView.getSyntheticItems());
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/AccessControl.java b/src/main/java/com/android/tools/r8/graph/AccessControl.java
index 428d0fd..43bbfa2 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessControl.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessControl.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
/**
@@ -24,6 +25,7 @@
clazz,
context,
appView.appInfo().getClassToFeatureSplitMap(),
+ appView.options(),
appView.appInfo().getStartupOrder(),
appView.getSyntheticItems());
}
@@ -32,6 +34,7 @@
DexClass clazz,
Definition context,
ClassToFeatureSplitMap classToFeatureSplitMap,
+ InternalOptions options,
StartupOrder startupOrder,
SyntheticItems syntheticItems) {
if (!clazz.isPublic() && !clazz.getType().isSamePackage(context.getContextType())) {
@@ -40,7 +43,11 @@
if (clazz.isProgramClass()
&& context.isProgramDefinition()
&& !classToFeatureSplitMap.isInBaseOrSameFeatureAs(
- clazz.asProgramClass(), context.asProgramDefinition(), startupOrder, syntheticItems)) {
+ clazz.asProgramClass(),
+ context.asProgramDefinition(),
+ options,
+ startupOrder,
+ syntheticItems)) {
return OptionalBool.UNKNOWN;
}
return OptionalBool.TRUE;
@@ -78,6 +85,7 @@
initialResolutionHolder,
context,
appInfo.getClassToFeatureSplitMap(),
+ appInfo.options(),
appInfo.getStartupOrder(),
appInfo.getSyntheticItems());
if (classAccessibility.isFalse()) {
diff --git a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
index 5b43e8d..94bb52d 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodResolutionResult.java
@@ -1272,6 +1272,7 @@
clazz,
context,
appInfo.getClassToFeatureSplitMap(),
+ appInfo.options(),
appInfo.getStartupOrder(),
appInfo.getSyntheticItems())
.isPossiblyFalse())),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
index f4a6d2a..dad5fd5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LibraryValidator;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.SemanticVersion;
import com.android.tools.r8.utils.Timing;
@@ -225,6 +226,7 @@
@Override
public MachineDesugaredLibrarySpecification toMachineSpecification(
DexApplication app, Timing timing) throws IOException {
+ LibraryValidator.validate(app, libraryCompilation, getRequiredCompilationApiLevel());
return this;
}
diff --git a/src/main/java/com/android/tools/r8/naming/PositionRangeAllocator.java b/src/main/java/com/android/tools/r8/naming/PositionRangeAllocator.java
new file mode 100644
index 0000000..957e660
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/PositionRangeAllocator.java
@@ -0,0 +1,71 @@
+// 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.naming;
+
+public abstract class PositionRangeAllocator {
+
+ private static final int MAX_POSITION = 256;
+ private static final int MAX_DELTA = 1;
+
+ final Range[] cache = new Range[MAX_POSITION];
+
+ public Range get(int index) {
+ return (index >= 0 && index < MAX_POSITION) ? cache[index] : new Range(index);
+ }
+
+ public static CardinalPositionRangeAllocator createCardinalPositionRangeAllocator() {
+ return new CardinalPositionRangeAllocator();
+ }
+
+ public static NonCardinalPositionRangeAllocator createNonCardinalPositionRangeAllocator() {
+ return new NonCardinalPositionRangeAllocator();
+ }
+
+ public static class CardinalPositionRangeAllocator extends PositionRangeAllocator {
+
+ private CardinalPositionRangeAllocator() {
+ super();
+ for (int i = 0; i < MAX_POSITION; i++) {
+ cache[i] = new Range(i);
+ }
+ }
+ }
+
+ public static class NonCardinalPositionRangeFixedDeltaCache extends PositionRangeAllocator {
+
+ public NonCardinalPositionRangeFixedDeltaCache(int delta) {
+ super();
+ for (int i = 0; i < MAX_POSITION; i++) {
+ cache[i] = new Range(i, i + delta);
+ }
+ }
+ }
+
+ public static class NonCardinalPositionRangeAllocator extends PositionRangeAllocator {
+
+ private final NonCardinalPositionRangeFixedDeltaCache[] cache =
+ new NonCardinalPositionRangeFixedDeltaCache[MAX_DELTA + 1];
+
+ private NonCardinalPositionRangeAllocator() {
+ for (int i = 0; i <= MAX_DELTA; i++) {
+ cache[i] = new NonCardinalPositionRangeFixedDeltaCache(i);
+ }
+ }
+
+ public Range get(int from, int to) {
+ if (from >= MAX_POSITION) {
+ return new Range(from, to);
+ }
+ int thisDelta = to - from;
+ if (thisDelta < 0) {
+ return new Range(from, to);
+ }
+ if (thisDelta > MAX_DELTA) {
+ return new Range(from, to);
+ }
+ return cache[thisDelta].get(from);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 6bde14bf..a3a3aa0 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
+import com.android.tools.r8.naming.PositionRangeAllocator.CardinalPositionRangeAllocator;
+import com.android.tools.r8.naming.PositionRangeAllocator.NonCardinalPositionRangeAllocator;
import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
@@ -68,6 +70,11 @@
private final boolean allowEmptyMappedRanges;
private final boolean allowExperimentalMapping;
+ private final CardinalPositionRangeAllocator cardinalRangeCache =
+ PositionRangeAllocator.createCardinalPositionRangeAllocator();
+ private final NonCardinalPositionRangeAllocator nonCardinalRangeCache =
+ PositionRangeAllocator.createNonCardinalPositionRangeAllocator();
+
@Override
public void close() throws IOException {
reader.close();
@@ -623,12 +630,12 @@
int from = parseNumber();
skipWhitespace();
if (peekChar(0) != ':') {
- return new Range(from);
+ return cardinalRangeCache.get(from);
}
expect(':');
skipWhitespace();
int to = parseNumber();
- return new Range(from, to);
+ return nonCardinalRangeCache.get(from, to);
}
private int parseNumber() {
diff --git a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
index 8545462..f292409 100644
--- a/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
+++ b/src/main/java/com/android/tools/r8/repackaging/Repackaging.java
@@ -347,7 +347,16 @@
DescriptorUtils.getBinaryNameFromJavaType(proguardConfiguration.getPackagePrefix());
PackageObfuscationMode packageObfuscationMode =
proguardConfiguration.getPackageObfuscationMode();
- if (packageObfuscationMode.isRepackageClasses()) {
+ if (!appView.options().isMinifying()) {
+ // Preserve full package name under destination package when not minifying
+ // (no matter which package obfuscation mode is used).
+ if (newPackageDescriptor.isEmpty()
+ || proguardConfiguration.getKeepPackageNamesPatterns().matches(pkg)
+ || mayHavePinnedPackagePrivateOrProtectedItem(pkg)) {
+ return pkg.getPackageDescriptor();
+ }
+ return newPackageDescriptor + DESCRIPTOR_PACKAGE_SEPARATOR + pkg.getPackageDescriptor();
+ } else if (packageObfuscationMode.isRepackageClasses()) {
return newPackageDescriptor;
} else if (packageObfuscationMode.isMinification()) {
// Always keep top-level classes since their packages can never be minified.
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 7199fcb..9785028 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -61,7 +61,7 @@
public boolean isRepackagingAllowed(
ProgramDefinition definition, GlobalKeepInfoConfiguration configuration) {
return configuration.isRepackagingEnabled()
- && internalIsMinificationAllowed()
+ && internalIsRepackagingAllowed()
&& (definition.getAccessFlags().isPublic()
|| !internalIsAccessModificationRequiredForRepackaging());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
index 9595b06..d9708ce 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -22,6 +22,7 @@
private final boolean allowAnnotationRemoval;
private final boolean allowMinification;
private final boolean allowOptimization;
+ private final boolean allowRepackaging;
private final boolean allowShrinking;
private final boolean allowSignatureRemoval;
private final boolean checkDiscarded;
@@ -32,6 +33,7 @@
boolean allowAnnotationRemoval,
boolean allowMinification,
boolean allowOptimization,
+ boolean allowRepackaging,
boolean allowShrinking,
boolean allowSignatureRemoval,
boolean checkDiscarded,
@@ -40,6 +42,7 @@
this.allowAnnotationRemoval = allowAnnotationRemoval;
this.allowMinification = allowMinification;
this.allowOptimization = allowOptimization;
+ this.allowRepackaging = allowRepackaging;
this.allowShrinking = allowShrinking;
this.allowSignatureRemoval = allowSignatureRemoval;
this.checkDiscarded = checkDiscarded;
@@ -52,6 +55,7 @@
builder.isAnnotationRemovalAllowed(),
builder.isMinificationAllowed(),
builder.isOptimizationAllowed(),
+ builder.isRepackagingAllowed(),
builder.isShrinkingAllowed(),
builder.isSignatureRemovalAllowed(),
builder.isCheckDiscardedEnabled(),
@@ -168,6 +172,10 @@
public abstract boolean isRepackagingAllowed(
ProgramDefinition definition, GlobalKeepInfoConfiguration configuration);
+ boolean internalIsRepackagingAllowed() {
+ return allowRepackaging;
+ }
+
boolean internalIsAccessModificationRequiredForRepackaging() {
return requireAccessModificationForRepackaging;
}
@@ -234,6 +242,7 @@
&& (allowAnnotationRemoval || !other.internalIsAnnotationRemovalAllowed())
&& (allowMinification || !other.internalIsMinificationAllowed())
&& (allowOptimization || !other.internalIsOptimizationAllowed())
+ && (allowRepackaging || !other.internalIsRepackagingAllowed())
&& (allowShrinking || !other.internalIsShrinkingAllowed())
&& (allowSignatureRemoval || !other.internalIsSignatureRemovalAllowed())
&& (!checkDiscarded || other.internalIsCheckDiscardedEnabled());
@@ -256,6 +265,7 @@
private boolean allowAccessModification;
private boolean allowAnnotationRemoval;
private boolean allowMinification;
+ private boolean allowRepackaging;
private boolean allowOptimization;
private boolean allowShrinking;
private boolean allowSignatureRemoval;
@@ -272,6 +282,7 @@
allowAnnotationRemoval = original.internalIsAnnotationRemovalAllowed();
allowMinification = original.internalIsMinificationAllowed();
allowOptimization = original.internalIsOptimizationAllowed();
+ allowRepackaging = original.internalIsRepackagingAllowed();
allowShrinking = original.internalIsShrinkingAllowed();
allowSignatureRemoval = original.internalIsSignatureRemovalAllowed();
checkDiscarded = original.internalIsCheckDiscardedEnabled();
@@ -284,6 +295,7 @@
disallowAnnotationRemoval();
disallowMinification();
disallowOptimization();
+ disallowRepackaging();
disallowShrinking();
disallowSignatureRemoval();
unsetCheckDiscarded();
@@ -296,6 +308,7 @@
allowAnnotationRemoval();
allowMinification();
allowOptimization();
+ allowRepackaging();
allowShrinking();
allowSignatureRemoval();
unsetCheckDiscarded();
@@ -323,6 +336,7 @@
&& isAnnotationRemovalAllowed() == other.internalIsAnnotationRemovalAllowed()
&& isMinificationAllowed() == other.internalIsMinificationAllowed()
&& isOptimizationAllowed() == other.internalIsOptimizationAllowed()
+ && isRepackagingAllowed() == other.internalIsRepackagingAllowed()
&& isShrinkingAllowed() == other.internalIsShrinkingAllowed()
&& isSignatureRemovalAllowed() == other.internalIsSignatureRemovalAllowed()
&& isCheckDiscardedEnabled() == other.internalIsCheckDiscardedEnabled()
@@ -354,6 +368,10 @@
return allowOptimization;
}
+ public boolean isRepackagingAllowed() {
+ return allowRepackaging;
+ }
+
public boolean isShrinkingAllowed() {
return allowShrinking;
}
@@ -375,6 +393,19 @@
return setAllowMinification(false);
}
+ public B setAllowRepackaging(boolean allowRepackaging) {
+ this.allowRepackaging = allowRepackaging;
+ return self();
+ }
+
+ public B allowRepackaging() {
+ return setAllowRepackaging(true);
+ }
+
+ public B disallowRepackaging() {
+ return setAllowRepackaging(false);
+ }
+
public B setAllowOptimization(boolean allowOptimization) {
this.allowOptimization = allowOptimization;
return self();
@@ -577,6 +608,11 @@
return self();
}
+ public J disallowRepackaging() {
+ builder.disallowRepackaging();
+ return self();
+ }
+
public J disallowOptimization() {
builder.disallowOptimization();
return self();
@@ -608,6 +644,7 @@
applyIf(!builder.isAnnotationRemovalAllowed(), Joiner::disallowAnnotationRemoval);
applyIf(!builder.isMinificationAllowed(), Joiner::disallowMinification);
applyIf(!builder.isOptimizationAllowed(), Joiner::disallowOptimization);
+ applyIf(!builder.isRepackagingAllowed(), Joiner::disallowRepackaging);
applyIf(!builder.isShrinkingAllowed(), Joiner::disallowShrinking);
applyIf(!builder.isSignatureRemovalAllowed(), Joiner::disallowSignatureRemoval);
applyIf(builder.isCheckDiscardedEnabled(), Joiner::setCheckDiscarded);
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index d804fa6..7275e72 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -1133,7 +1133,8 @@
if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, clazz.getReference())
- .disallowMinification();
+ .disallowMinification()
+ .disallowRepackaging();
}
}
@@ -1635,7 +1636,15 @@
if (appView.options().isMinificationEnabled() && !modifiers.allowsObfuscation) {
dependentMinimumKeepInfo
.getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
- .disallowMinification();
+ .disallowMinification()
+ .disallowRepackaging();
+ context.markAsUsed();
+ }
+
+ if (appView.options().isRepackagingEnabled() && !modifiers.allowsObfuscation) {
+ dependentMinimumKeepInfo
+ .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference())
+ .disallowRepackaging();
context.markAsUsed();
}
@@ -1971,7 +1980,8 @@
void shouldNotBeMinified(ProgramDefinition definition) {
getDependentMinimumKeepInfo()
.getOrCreateUnconditionalMinimumKeepInfoFor(definition.getReference())
- .disallowMinification();
+ .disallowMinification()
+ .disallowRepackaging();
}
public boolean verifyKeptFieldsAreAccessedAndLive(AppView<AppInfoWithLiveness> appView) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 843a0f3..323c745 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.ConsumerUtils;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
@@ -488,6 +489,22 @@
return oracle.getSynthesizingContexts(clazz);
}
+ public Map<DexType, List<DexProgramClass>> computeSyntheticContextsToSyntheticClasses(
+ AppView<?> appView) {
+ Map<DexType, List<DexProgramClass>> syntheticContextsToSyntheticClasses =
+ new IdentityHashMap<>();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (isSyntheticClass(clazz)) {
+ for (DexType synthesizingContextType : getSynthesizingContextTypes(clazz.getType())) {
+ syntheticContextsToSyntheticClasses
+ .computeIfAbsent(synthesizingContextType, ignoreKey -> new ArrayList<>())
+ .add(clazz);
+ }
+ }
+ }
+ return syntheticContextsToSyntheticClasses;
+ }
+
public interface SynthesizingContextOracle {
Set<DexReference> getSynthesizingContexts(DexProgramClass clazz);
@@ -528,18 +545,25 @@
private SynthesizingContext getSynthesizingContext(
ProgramDefinition context, AppView<?> appView) {
+ InternalOptions options = appView.options();
if (appView.hasClassHierarchy()) {
AppInfoWithClassHierarchy appInfo = appView.appInfoWithClassHierarchy();
return getSynthesizingContext(
- context, appInfo.getClassToFeatureSplitMap(), appInfo.getStartupOrder());
+ context, appInfo.getClassToFeatureSplitMap(), options, appInfo.getStartupOrder());
}
return getSynthesizingContext(
- context, ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(), StartupOrder.empty());
+ context,
+ ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
+ options,
+ StartupOrder.empty());
}
/** Used to find the synthesizing context for a new synthetic that is about to be created. */
private SynthesizingContext getSynthesizingContext(
- ProgramDefinition context, ClassToFeatureSplitMap featureSplits, StartupOrder startupOrder) {
+ ProgramDefinition context,
+ ClassToFeatureSplitMap featureSplits,
+ InternalOptions options,
+ StartupOrder startupOrder) {
DexType contextType = context.getContextType();
SyntheticDefinition<?, ?, ?> existingDefinition = pending.definitions.get(contextType);
if (existingDefinition != null) {
@@ -555,7 +579,7 @@
.getContext();
}
// This context is not nested in an existing synthetic context so create a new "leaf" context.
- FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, startupOrder, this);
+ FeatureSplit featureSplit = featureSplits.getFeatureSplit(context, options, startupOrder, this);
return SynthesizingContext.fromNonSyntheticInputContext(context, featureSplit);
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index fbf344b..14b4d67 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -37,7 +37,6 @@
import com.android.tools.r8.experimental.startup.StartupOrder;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.features.FeatureSplitConfiguration;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
@@ -466,7 +465,7 @@
return programResourcesMainDescriptor.get(resource);
}
- public void dump(Path output, DumpOptions options, Reporter reporter, DexItemFactory factory) {
+ public void dump(Path output, DumpOptions dumpOptions, InternalOptions options) {
int nextDexIndex = 0;
OpenOption[] openOptions =
new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
@@ -476,26 +475,26 @@
writeToZipStream(
out,
dumpBuildPropertiesFileName,
- options.getBuildPropertiesFileContent().getBytes(),
+ dumpOptions.getBuildPropertiesFileContent().getBytes(),
ZipEntry.DEFLATED);
- if (options.getDesugaredLibraryJsonSource() != null) {
+ if (dumpOptions.getDesugaredLibraryJsonSource() != null) {
writeToZipStream(
out,
dumpDesugaredLibraryFileName,
- options.getDesugaredLibraryJsonSource().getBytes(),
+ dumpOptions.getDesugaredLibraryJsonSource().getBytes(),
ZipEntry.DEFLATED);
- if (options.dumpInputToFile()) {
- reporter.warning(
+ if (dumpOptions.dumpInputToFile()) {
+ options.reporter.warning(
"Dumping a compilation with desugared library on a file may prevent reproduction,"
+ " use dumpInputToDirectory property instead.");
}
}
- if (options.getParsedProguardConfiguration() != null) {
- String proguardConfig = options.getParsedProguardConfiguration();
+ if (dumpOptions.getParsedProguardConfiguration() != null) {
+ String proguardConfig = dumpOptions.getParsedProguardConfiguration();
writeToZipStream(out, dumpConfigFileName, proguardConfig.getBytes(), ZipEntry.DEFLATED);
}
if (proguardMapInputData != null) {
- reporter.warning(
+ options.reporter.warning(
"Dumping proguard map input data may have side effects due to I/O on Paths.");
writeToZipStream(
out,
@@ -506,7 +505,7 @@
if (hasMainDexList()) {
List<String> mainDexList = new ArrayList<>();
if (hasMainDexListResources()) {
- reporter.warning(
+ options.reporter.warning(
"Dumping main dex list resources may have side effects due to I/O on Paths.");
for (StringResource mainDexListResource : getMainDexListResources()) {
mainDexList.add(mainDexListResource.getString());
@@ -518,25 +517,24 @@
String join = StringUtils.join("\n", mainDexList);
writeToZipStream(out, dumpMainDexListResourceFileName, join.getBytes(), ZipEntry.DEFLATED);
}
- if (options.hasMainDexKeepRules()) {
+ if (dumpOptions.hasMainDexKeepRules()) {
writeToZipStream(
out,
dumpMainDexRulesResourceFileName,
- StringUtils.joinLines(options.getMainDexKeepRules()).getBytes(),
+ StringUtils.joinLines(dumpOptions.getMainDexKeepRules()).getBytes(),
ZipEntry.DEFLATED);
}
nextDexIndex =
dumpProgramResources(
dumpProgramFileName,
- options.getFeatureSplitConfiguration(),
+ dumpOptions.getFeatureSplitConfiguration(),
nextDexIndex,
out,
- reporter,
- factory);
+ options);
nextDexIndex = dumpClasspathResources(nextDexIndex, out);
nextDexIndex = dumpLibraryResources(nextDexIndex, out);
} catch (IOException | ResourceException e) {
- throw reporter.fatalError(new ExceptionDiagnostic(e));
+ throw options.reporter.fatalError(new ExceptionDiagnostic(e));
}
}
@@ -584,10 +582,8 @@
FeatureSplitConfiguration featureSplitConfiguration,
int nextDexIndex,
ZipOutputStream out,
- Reporter reporter,
- DexItemFactory dexItemFactory)
+ InternalOptions options)
throws IOException, ResourceException {
-
Map<FeatureSplit, String> featureSplitArchiveNames =
dumpFeatureSplitFileNames(featureSplitConfiguration);
Map<FeatureSplit, ByteArrayOutputStream> featureSplitArchiveByteStreams =
@@ -596,7 +592,7 @@
try {
ClassToFeatureSplitMap classToFeatureSplitMap =
ClassToFeatureSplitMap.createInitialClassToFeatureSplitMap(
- dexItemFactory, featureSplitConfiguration, reporter);
+ options.dexItemFactory(), featureSplitConfiguration, options.reporter);
if (featureSplitConfiguration != null) {
for (FeatureSplit featureSplit : featureSplitConfiguration.getFeatureSplits()) {
ByteArrayOutputStream archiveByteStream = new ByteArrayOutputStream();
@@ -624,11 +620,11 @@
nextDexIndex,
classDescriptor -> {
if (featureSplitConfiguration != null) {
- DexType type = dexItemFactory.createType(classDescriptor);
+ DexType type = options.dexItemFactory().createType(classDescriptor);
SyntheticItems syntheticItems = null;
FeatureSplit featureSplit =
classToFeatureSplitMap.getFeatureSplit(
- type, StartupOrder.empty(), syntheticItems);
+ type, options, StartupOrder.empty(), syntheticItems);
if (featureSplit != null && !featureSplit.isBase()) {
return featureSplitArchiveOutputStreams.get(featureSplit);
}
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 0c68c28..4653b96 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -443,9 +443,6 @@
// Compute the marker to be placed in the main dex file.
private Marker createMarker(Tool tool) {
- if (tool == Tool.D8 && testing.dontCreateMarkerInD8) {
- return null;
- }
Marker marker =
new Marker(tool)
.setVersion(Version.LABEL)
@@ -699,7 +696,10 @@
@Override
public boolean isRepackagingEnabled() {
- return proguardConfiguration.getPackageObfuscationMode().isSome() && isMinifying();
+ return !debug
+ && proguardConfiguration != null
+ && proguardConfiguration.getPackageObfuscationMode().isSome()
+ && (isMinifying() || !isForceProguardCompatibilityEnabled());
}
@Override
@@ -1772,6 +1772,7 @@
public static class TestingOptions {
+ public boolean neverReuseCfLocalRegisters = false;
private boolean hasReadCheckDeterminism = false;
private DeterminismChecker determinismChecker = null;
@@ -1892,7 +1893,6 @@
public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false;
public boolean invertConditionals = false;
public boolean placeExceptionalBlocksLast = false;
- public boolean dontCreateMarkerInD8 = false;
public boolean forceJumboStringProcessing = false;
public boolean forcePcBasedEncoding = false;
public int pcBasedDebugEncodingOverheadThreshold =
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 74b7b06..a0a2a74 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -57,6 +57,9 @@
import com.android.tools.r8.naming.MemberNaming;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.PositionRangeAllocator;
+import com.android.tools.r8.naming.PositionRangeAllocator.CardinalPositionRangeAllocator;
+import com.android.tools.r8.naming.PositionRangeAllocator.NonCardinalPositionRangeAllocator;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.naming.ProguardMapSupplier.ProguardMapId;
import com.android.tools.r8.naming.Range;
@@ -509,6 +512,11 @@
? new NativePcSupport()
: new Pc2PcMappingSupport(appView.options().allowDiscardingResidualDebugInfo());
+ CardinalPositionRangeAllocator cardinalRangeCache =
+ PositionRangeAllocator.createCardinalPositionRangeAllocator();
+ NonCardinalPositionRangeAllocator nonCardinalRangeCache =
+ PositionRangeAllocator.createNonCardinalPositionRangeAllocator();
+
// Collect which files contain which classes that need to have their line numbers optimized.
for (DexProgramClass clazz : appView.appInfo().classes()) {
boolean isSyntheticClass = appView.getSyntheticItems().isSyntheticClass(clazz);
@@ -718,10 +726,11 @@
&& definition.getCode().asDexCode().getDebugInfo()
== DexDebugInfoForSingleLineMethod.getInstance()) {
assert firstPosition.originalLine == lastPosition.originalLine;
- obfuscatedRange = new Range(0, MAX_LINE_NUMBER);
+ obfuscatedRange = nonCardinalRangeCache.get(0, MAX_LINE_NUMBER);
} else {
obfuscatedRange =
- new Range(firstPosition.obfuscatedLine, lastPosition.obfuscatedLine);
+ nonCardinalRangeCache.get(
+ firstPosition.obfuscatedLine, lastPosition.obfuscatedLine);
}
ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.computeIfAbsent();
MappedRange lastMappedRange =
@@ -732,9 +741,11 @@
firstPosition.method,
obfuscatedName,
obfuscatedRange,
- new Range(firstPosition.originalLine, lastPosition.originalLine),
+ nonCardinalRangeCache.get(
+ firstPosition.originalLine, lastPosition.originalLine),
firstPosition.caller,
- prunedInlinedClasses);
+ prunedInlinedClasses,
+ cardinalRangeCache);
for (MappingInformation info : methodMappingInfo) {
lastMappedRange.addMappingInformation(info, Unreachable::raise);
}
@@ -758,10 +769,12 @@
classNamingBuilder,
position.getMethod(),
obfuscatedName,
- new Range(placeHolderLineToBeFixed, placeHolderLineToBeFixed),
- new Range(position.getLine(), position.getLine()),
+ nonCardinalRangeCache.get(
+ placeHolderLineToBeFixed, placeHolderLineToBeFixed),
+ nonCardinalRangeCache.get(position.getLine(), position.getLine()),
position.getCallerPosition(),
- prunedInlinedClasses);
+ prunedInlinedClasses,
+ cardinalRangeCache);
});
outlinesToFix
.computeIfAbsent(
@@ -838,7 +851,8 @@
Range obfuscatedRange,
Range originalLine,
Position caller,
- Map<DexType, String> prunedInlineHolder) {
+ Map<DexType, String> prunedInlineHolder,
+ CardinalPositionRangeAllocator cardinalRangeCache) {
MappedRange lastMappedRange =
classNamingBuilder.addMappedRange(
obfuscatedRange,
@@ -859,7 +873,8 @@
classNamingBuilder.addMappedRange(
obfuscatedRange,
getOriginalMethodSignature.apply(caller.getMethod()),
- new Range(Math.max(caller.getLine(), 0)), // Prevent against "no-position".
+ cardinalRangeCache.get(
+ Math.max(caller.getLine(), 0)), // Prevent against "no-position".
obfuscatedName);
if (caller.isRemoveInnerFramesIfThrowingNpe()) {
lastMappedRange.addMappingInformation(
diff --git a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
index 28be79a..4fda093 100644
--- a/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
+++ b/src/test/java/com/android/tools/r8/AndroidAppDumpsTest.java
@@ -68,7 +68,7 @@
.build();
Path dumpFile = temp.newFolder().toPath().resolve("dump.zip");
- appIn.dump(dumpFile, options.dumpOptions, options.reporter, options.dexItemFactory());
+ appIn.dump(dumpFile, options.dumpOptions, options);
AndroidApp appOut = AndroidApp.builder(options.reporter).addDump(dumpFile).build();
assertEquals(1, appOut.getClassProgramResourcesForTesting().size());
diff --git a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
index 001c239..3cd934b 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
@@ -75,6 +75,7 @@
.setProguardMapProducer(
ProguardMapProducer.fromPath(
dependencyRoot.resolve("r8lib.jar.map")))
+ .setLoadAllDefinitions(false)
.build())
.setStackTrace(stackTrace)
.setRetracedStackTraceConsumer(retraced::addAll)
diff --git a/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
index 32ce06a..5108c8d 100644
--- a/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/cf/CfDebugLocalStackMapVerificationTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertThrows;
import com.android.tools.r8.CompilationFailedException;
@@ -11,6 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -23,8 +25,8 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
+/** Regression tests for b/237567012 */
@RunWith(Parameterized.class)
-/** This is a regresson test for b/237567012 */
public class CfDebugLocalStackMapVerificationTest extends TestBase {
@Parameter() public TestParameters parameters;
@@ -34,6 +36,43 @@
return getTestParameters().withCfRuntimes().build();
}
+ static class SmallRepro {
+
+ public static void main(String[] args) {
+ RuntimeException x = new RuntimeException("FOO");
+ RuntimeException c = null;
+ try {
+ c = x;
+ throw c;
+ } catch (RuntimeException e) {
+ System.out.println(c);
+ }
+ }
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForJvm()
+ .addProgramClasses(SmallRepro.class)
+ .run(parameters.getRuntime(), SmallRepro.class)
+ .assertSuccessWithOutputThatMatches(containsString("FOO"));
+ }
+
+ @Test
+ public void testSmallReproD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClasses(SmallRepro.class)
+ .setMinApi(AndroidApiLevel.B)
+ .addOptionsModification(
+ options -> {
+ options.testing.forceIRForCfToCfDesugar = true;
+ options.testing.neverReuseCfLocalRegisters = true;
+ })
+ .run(parameters.getRuntime(), SmallRepro.class)
+ // TODO(b/237567012): Run should succeed with printing of FOO.
+ .assertFailureWithErrorThatThrows(VerifyError.class);
+ }
+
@Test
public void testR8() throws Exception {
// TODO(b/237567012): We should not fail compilation.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
index 2a325ad..30b9700 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingWithStartupClassesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.experimental.startup.StartupClass;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
@@ -26,14 +27,18 @@
public TestParameters parameters;
@Parameter(1)
- public List<Class<?>> startupClasses;
+ public boolean includeStartupClasses;
- @Parameters(name = "{0}, startup classes: {1}")
+ @Parameters(name = "{0}, include startup classes: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(),
- ImmutableList.of(
- Collections.emptyList(), ImmutableList.of(StartupA.class, StartupB.class)));
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ private List<Class<?>> getStartupClasses() {
+ return includeStartupClasses
+ ? Collections.emptyList()
+ : ImmutableList.of(StartupA.class, StartupB.class);
}
@Test
@@ -50,20 +55,21 @@
StartupConfiguration.builder()
.apply(
builder ->
- startupClasses.forEach(
- startupClass ->
- builder.addStartupClass(
- StartupClass.dexBuilder()
- .setClassReference(
- toDexType(startupClass, dexItemFactory))
- .build())))
+ getStartupClasses()
+ .forEach(
+ startupClass ->
+ builder.addStartupClass(
+ StartupClass.dexBuilder()
+ .setClassReference(
+ toDexType(startupClass, dexItemFactory))
+ .build())))
.build());
})
.addHorizontallyMergedClassesInspector(
inspector ->
inspector
.applyIf(
- startupClasses.isEmpty(),
+ getStartupClasses().isEmpty(),
i ->
i.assertIsCompleteMergeGroup(
StartupA.class,
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index f6f59e8..7d13c36 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.TestBase;
@@ -204,7 +205,7 @@
@Test
public void testPerFileIntermediate() throws Exception {
- ProcessResult result = runDoublePerFileCompilation(true);
+ ProcessResult result = runDoublePerFileCompilation(Backend.CF, true);
assertEquals(result.toString(), 0, result.exitCode);
assertEquals(EXPECTED, result.stdout);
}
@@ -212,7 +213,7 @@
@Test
public void testPerFileNonIntermediate() throws Exception {
try {
- runDoublePerFileCompilation(false);
+ runDoublePerFileCompilation(Backend.CF, false);
fail("Should expect the compilation to fail.");
} catch (CompilationFailedException e) {
assertThat(
@@ -221,26 +222,60 @@
}
}
- public ProcessResult runDoublePerFileCompilation(boolean intermediate) throws Exception {
+ @Test
+ public void testPerFileNonIntermediateDex() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ try {
+ runDoublePerFileCompilation(Backend.DEX, false);
+ fail("Should expect the compilation to fail.");
+ } catch (CompilationFailedException e) {
+ assertThat(
+ e.getCause().getMessage(),
+ containsString("Attempt at compiling intermediate artifact without its context"));
+ }
+ }
+
+ public ProcessResult runDoublePerFileCompilation(Backend firstRoundOutput, boolean intermediate)
+ throws Exception {
List<byte[]> outputsRoundOne = new ArrayList<>();
- testForD8(Backend.CF)
+ testForD8(firstRoundOutput)
.addProgramClasses(CLASSES)
.setMinApi(parameters.getApiLevel())
.setIntermediate(true /* First round is always intermediate. */)
.setProgramConsumer(
- new ClassFileConsumer.ForwardingConsumer(null) {
- @Override
- public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
- outputsRoundOne.add(data.copyByteData());
- }
- })
+ firstRoundOutput.isCf()
+ ? new ClassFileConsumer.ForwardingConsumer(null) {
+ @Override
+ public void accept(
+ ByteDataView data, String descriptor, DiagnosticsHandler handler) {
+ outputsRoundOne.add(data.copyByteData());
+ }
+ }
+ : new DexFilePerClassFileConsumer.ForwardingConsumer(null) {
+ @Override
+ public void accept(
+ String primaryClassDescriptor,
+ ByteDataView data,
+ Set<String> descriptors,
+ DiagnosticsHandler handler) {
+ outputsRoundOne.add(data.copyByteData());
+ }
+
+ @Override
+ public boolean combineSyntheticClassesWithPrimaryClass() {
+ return false;
+ }
+ })
.compile();
List<Path> outputsRoundTwo = new ArrayList<>();
for (byte[] bytes : outputsRoundOne) {
outputsRoundTwo.add(
testForD8(parameters.getBackend())
- .addProgramClassFileData(bytes)
+ .applyIf(
+ firstRoundOutput.isCf(),
+ b -> b.addProgramClassFileData(bytes),
+ b -> b.addProgramDexFileData(bytes))
.setMinApi(parameters.getApiLevel())
.setIntermediate(intermediate)
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
new file mode 100644
index 0000000..20f26fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ConcurrentHashMapSubclassV2Test.java
@@ -0,0 +1,85 @@
+// 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.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.R8_L8SHRINK;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConcurrentHashMapSubclassV2Test extends DesugaredLibraryTestBase {
+
+ private static final String EXPECTED_RESULT = StringUtils.lines("foo");
+
+ private final TestParameters parameters;
+ private final CompilationSpecification compilationSpecification;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationMode compilationMode;
+
+ @Parameters(name = "{0}, spec: {1}, {2}, {3}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ getJdk8Jdk11(),
+ DEFAULT_SPECIFICATIONS,
+ CompilationMode.values());
+ }
+
+ public ConcurrentHashMapSubclassV2Test(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification,
+ CompilationMode compilationMode) {
+ this.parameters = parameters;
+ this.compilationSpecification = compilationSpecification;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationMode = compilationMode;
+ }
+
+ @Test
+ public void test() throws Exception {
+ Assume.assumeFalse(
+ "b/237701688", compilationMode.isDebug() && compilationSpecification == R8_L8SHRINK);
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addInnerClasses(getClass())
+ .setMode(compilationMode)
+ .addKeepMainRule(Executor.class)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @SuppressWarnings("unchecked")
+ static class Executor {
+ public static void main(String[] args) {
+ StringListConcurrentHashMap<String> map = new StringListConcurrentHashMap<>();
+ map.putKeyAndArray("foo");
+ System.out.println(map.keys().nextElement());
+ }
+ }
+
+ static class StringListConcurrentHashMap<K> extends ConcurrentHashMap<K, List<String>> {
+ StringListConcurrentHashMap() {
+ super();
+ }
+
+ void putKeyAndArray(K key) {
+ this.putIfAbsent(key, new ArrayList<>());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index 5a2c8b0..28d4db2 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.desugar.desugaredlibrary.test;
import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.L8TestBuilder;
@@ -164,6 +165,11 @@
return this;
}
+ public DesugaredLibraryTestBuilder<T> setMode(CompilationMode mode) {
+ builder.setMode(mode);
+ return this;
+ }
+
private void withR8TestBuilder(Consumer<R8TestBuilder<?>> consumer) {
if (!builder.isTestShrinkerBuilder()) {
return;
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageClassWithDontObfuscateKeepPackageNameOnTargetTest.java
similarity index 67%
copy from src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
copy to src/test/java/com/android/tools/r8/repackage/RepackageClassWithDontObfuscateKeepPackageNameOnTargetTest.java
index ce8088f..6fa0787 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageClassWithDontObfuscateKeepPackageNameOnTargetTest.java
@@ -5,9 +5,10 @@
package com.android.tools.r8.repackage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
@@ -18,11 +19,11 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class RepackageClassWithKeepPackageNameOnTargetTest extends RepackageTestBase {
+public class RepackageClassWithDontObfuscateKeepPackageNameOnTargetTest extends RepackageTestBase {
private static final String DESTINATION_PACKAGE = "other.package";
- public RepackageClassWithKeepPackageNameOnTargetTest(
+ public RepackageClassWithDontObfuscateKeepPackageNameOnTargetTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@@ -34,29 +35,25 @@
@Test
public void testR8() throws Exception {
- // TODO(b/241220445): Should be able to relocate when having -dontobfuscate in some way to
- // support mainline.
+ String originalPackage = DescriptorUtils.getPackageNameFromBinaryName(binaryName(Foo.class));
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.apply(this::configureRepackaging)
+ .addDontObfuscate()
+ .addKeepPackageNamesRule(originalPackage)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Foo::foo()")
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(Foo.class);
assertThat(clazz, isPresent());
- assertThat(clazz.getFinalName(), startsWith(DESTINATION_PACKAGE));
- String relocatedPackageSuffix =
- DescriptorUtils.getPackageBinaryNameFromJavaType(
- clazz.getFinalName().substring(DESTINATION_PACKAGE.length() + 1));
- String originalPackage =
- DescriptorUtils.getPackageBinaryNameFromJavaType(clazz.getOriginalName());
- // TODO(b/241220445): Have a configuration where the suffix is identicial to the
- // original.
- assertNotEquals(relocatedPackageSuffix, originalPackage);
+ assertThat(clazz.getFinalName(), not(startsWith(DESTINATION_PACKAGE)));
+ String finalPackage =
+ DescriptorUtils.getPackageNameFromBinaryName(clazz.getFinalBinaryName());
+ assertEquals(originalPackage, finalPackage);
});
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageClassesWithDontObfuscateTest.java
similarity index 81%
rename from src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
rename to src/test/java/com/android/tools/r8/repackage/RepackageClassesWithDontObfuscateTest.java
index ce8088f..3a36aea 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageClassWithKeepPackageNameOnTargetTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageClassesWithDontObfuscateTest.java
@@ -7,7 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
@@ -18,11 +18,11 @@
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
-public class RepackageClassWithKeepPackageNameOnTargetTest extends RepackageTestBase {
+public class RepackageClassesWithDontObfuscateTest extends RepackageTestBase {
private static final String DESTINATION_PACKAGE = "other.package";
- public RepackageClassWithKeepPackageNameOnTargetTest(
+ public RepackageClassesWithDontObfuscateTest(
String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
super(flattenPackageHierarchyOrRepackageClasses, parameters);
}
@@ -34,29 +34,26 @@
@Test
public void testR8() throws Exception {
- // TODO(b/241220445): Should be able to relocate when having -dontobfuscate in some way to
- // support mainline.
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.apply(this::configureRepackaging)
+ .addDontObfuscate()
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Foo::foo()")
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(Foo.class);
assertThat(clazz, isPresent());
- assertThat(clazz.getFinalName(), startsWith(DESTINATION_PACKAGE));
+ assertThat(clazz.getFinalName(), startsWith(DESTINATION_PACKAGE + "."));
String relocatedPackageSuffix =
DescriptorUtils.getPackageBinaryNameFromJavaType(
clazz.getFinalName().substring(DESTINATION_PACKAGE.length() + 1));
String originalPackage =
DescriptorUtils.getPackageBinaryNameFromJavaType(clazz.getOriginalName());
- // TODO(b/241220445): Have a configuration where the suffix is identicial to the
- // original.
- assertNotEquals(relocatedPackageSuffix, originalPackage);
+ assertEquals(relocatedPackageSuffix, originalPackage);
});
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
index 5ece41d..39b3d43 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
@@ -7,11 +7,14 @@
import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.ProguardVersion;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.google.common.collect.ImmutableList;
@@ -70,15 +73,23 @@
}
@Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .enableInliningAnnotations()
- .apply(this::configureRepackaging)
- .noMinification()
- .compile()
+ public void testR8Full() throws Exception {
+ setup(testForR8(parameters.getBackend()))
+ .inspect(
+ inspector -> {
+ ClassSubject aClass = inspector.clazz(A.class);
+ assertThat(aClass, isPresentAndRenamed());
+ })
+ .run(
+ parameters.getRuntime(),
+ Main.class,
+ getRepackagePackage() + "." + A.class.getTypeName())
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8Compat() throws Exception {
+ setup(testForR8Compat(parameters.getBackend()))
.inspect(
inspector -> {
ClassSubject aClass = inspector.clazz(A.class);
@@ -88,6 +99,17 @@
.assertSuccessWithOutputLines(EXPECTED);
}
+ private R8TestCompileResult setup(R8TestBuilder<?> r8TestBuilder) throws Exception {
+ return r8TestBuilder
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .apply(this::configureRepackaging)
+ .noMinification()
+ .compile();
+ }
+
public static class A {
@NeverInline
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 d57a5ab..a433265 100644
--- a/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
+++ b/src/test/java/com/android/tools/r8/startup/MinimalStartupDexTest.java
@@ -12,12 +12,18 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.experimental.startup.StartupClass;
-import com.android.tools.r8.experimental.startup.StartupConfiguration;
+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;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -40,6 +46,18 @@
@Test
public void test() throws Exception {
+ List<StartupItem<ClassReference, MethodReference, ?>> startupList = new ArrayList<>();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .apply(StartupTestingUtils.enableStartupInstrumentationUsingLogcat(parameters))
+ .release()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addRunClasspathFiles(StartupTestingUtils.getAndroidUtilLog(temp))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(StartupTestingUtils.removeStartupListFromStdout(startupList::add))
+ .assertSuccessWithOutputLines(getExpectedOutput());
+
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepClassAndMembersRules(Main.class)
@@ -48,25 +66,27 @@
options
.getStartupOptions()
.setEnableMinimalStartupDex(true)
- .setEnableStartupCompletenessCheckForTesting()
- .setStartupConfiguration(
- StartupConfiguration.builder()
- .addStartupClass(
- StartupClass.dexBuilder()
- .setClassReference(
- toDexType(Main.class, options.dexItemFactory()))
- .build())
- .addStartupClass(
- StartupClass.dexBuilder()
- .setClassReference(
- toDexType(AStartupClass.class, options.dexItemFactory()))
- .build())
- .build()))
+ .setEnableStartupCompletenessCheckForTesting())
.enableInliningAnnotations()
+ .apply(testBuilder -> StartupTestingUtils.setStartupConfiguration(testBuilder, startupList))
.setMinApi(parameters.getApiLevel())
.compile()
.inspectMultiDex(
primaryDexInspector -> {
+ // Main should be in the primary dex.
+ ClassSubject mainClassSubject = primaryDexInspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertTrue(
+ mainMethodSubject.streamInstructions().noneMatch(InstructionSubject::isThrow));
+
+ MethodSubject onClickMethodSubject = mainClassSubject.uniqueMethodWithName("onClick");
+ assertThat(onClickMethodSubject, isPresent());
+ assertTrue(
+ onClickMethodSubject.streamInstructions().anyMatch(InstructionSubject::isThrow));
+
// StartupClass should be in the primary dex.
ClassSubject startupClassSubject = primaryDexInspector.clazz(AStartupClass.class);
assertThat(startupClassSubject, isPresent());
@@ -99,7 +119,11 @@
.anyMatch(InstructionSubject::isThrow));
})
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("foo");
+ .assertSuccessWithOutputLines(getExpectedOutput());
+ }
+
+ private List<String> getExpectedOutput() {
+ return ImmutableList.of("foo");
}
static class Main {
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 7ae582f..0cce865 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticPlacementTest.java
@@ -48,14 +48,18 @@
public boolean enableMinimalStartupDex;
@Parameter(2)
+ public boolean enableStartupCompletenessCheck;
+
+ @Parameter(3)
public boolean useLambda;
- @Parameters(name = "{0}, minimal startup dex: {1}, use lambda: {2}")
+ @Parameters(name = "{0}, minimal startup dex: {1}, completeness check: {2}, use lambda: {3}")
public static List<Object[]> data() {
return buildParameters(
// N so that java.util.function.Consumer is present.
getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.N).build(),
BooleanUtils.values(),
+ BooleanUtils.values(),
BooleanUtils.values());
}
@@ -80,7 +84,10 @@
.addKeepClassAndMembersRules(A.class, B.class, C.class)
.addOptionsModification(
options -> {
- options.getStartupOptions().setEnableMinimalStartupDex(enableMinimalStartupDex);
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex(enableMinimalStartupDex)
+ .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
options
.getTestingOptions()
.setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
@@ -90,7 +97,10 @@
.compile()
.inspectMultiDex(this::inspectPrimaryDex, this::inspectSecondaryDex)
.run(parameters.getRuntime(), Main.class, Boolean.toString(useLambda))
- .assertSuccessWithOutputLines(getExpectedOutput());
+ .applyIf(
+ enableStartupCompletenessCheck && useLambda,
+ runResult -> runResult.assertFailureWithErrorThatThrows(NullPointerException.class),
+ runResult -> runResult.assertSuccessWithOutputLines(getExpectedOutput()));
}
private List<String> getExpectedOutput() {
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 3087653..13c7ca7 100644
--- a/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
+++ b/src/test/java/com/android/tools/r8/startup/StartupSyntheticWithoutContextTest.java
@@ -47,11 +47,15 @@
@Parameter(1)
public boolean enableMinimalStartupDex;
- @Parameters(name = "{0}, minimal startup dex: {1}")
+ @Parameter(2)
+ public boolean enableStartupCompletenessCheck;
+
+ @Parameters(name = "{0}, minimal startup dex: {1}, completeness check: {2}")
public static List<Object[]> data() {
return buildParameters(
// N so that java.util.function.Consumer is present.
getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.N).build(),
+ BooleanUtils.values(),
BooleanUtils.values());
}
@@ -76,7 +80,10 @@
.addKeepClassAndMembersRules(A.class, C.class)
.addOptionsModification(
options -> {
- options.getStartupOptions().setEnableMinimalStartupDex(enableMinimalStartupDex);
+ options
+ .getStartupOptions()
+ .setEnableMinimalStartupDex(enableMinimalStartupDex)
+ .setEnableStartupCompletenessCheckForTesting(enableStartupCompletenessCheck);
options
.getTestingOptions()
.setMixedSectionLayoutStrategyInspector(getMixedSectionLayoutInspector());
diff --git a/tools/r8_release.py b/tools/r8_release.py
index c00c15c..56828c0 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -392,7 +392,7 @@
if match_count != 3:
print(("Could not find the previous -dev release string to replace in " +
"METADATA. Expected to find is mentioned 3 times. Please update %s " +
- "manually and run again with options --google " +
+ "manually and run again with options --google3 " +
"--use-existing-work-branch.") % metadata_path)
sys.exit(1)
sed(version_match_regexp, options.version, metadata_path)
diff --git a/tools/startup/adb_utils.py b/tools/startup/adb_utils.py
index 56ad643..2369aa7 100755
--- a/tools/startup/adb_utils.py
+++ b/tools/startup/adb_utils.py
@@ -254,9 +254,12 @@
args.append('-W')
cmd = create_adb_cmd(args, device_id)
stdout = subprocess.check_output(cmd).decode('utf-8').strip()
- expected_stdout = (
- 'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
- assert stdout.startswith(expected_stdout), 'was %s' % stdout
+ if activity.startswith(app_id):
+ expected_stdout = (
+ 'Starting: Intent { cmp=%s/.%s }' % (app_id, activity[len(app_id)+1:]))
+ else:
+ expected_stdout = 'Starting: Intent { cmp=%s/%s }' % (app_id, activity)
+ assert stdout.startswith(expected_stdout), 'was %s, expected %s' % (stdout, expected_stdout)
lines = stdout.splitlines()
result = {}
for line in lines:
@@ -337,11 +340,16 @@
stderr = process_result.stderr.decode('utf-8')
if process_result.returncode == 0:
assert 'Success' in stdout
+ elif stdout.startswith('cmd: Failure calling service package: Broken pipe'):
+ assert app_id == 'com.google.android.youtube'
+ print('Waiting after broken pipe')
+ time.sleep(15)
else:
expected_error = (
'java.lang.IllegalArgumentException: Unknown package: %s' % app_id)
assert 'Failure [DELETE_FAILED_INTERNAL_ERROR]' in stdout \
- or expected_error in stderr
+ or expected_error in stderr, \
+ 'stdout: %s, stderr: %s' % (stdout, stderr)
def unlock(device_id=None, device_pin=None):
ensure_screen_on(device_id)
diff --git a/tools/startup/generate_startup_descriptors.py b/tools/startup/generate_startup_descriptors.py
index 26846bc..d4a33d0 100755
--- a/tools/startup/generate_startup_descriptors.py
+++ b/tools/startup/generate_startup_descriptors.py
@@ -25,13 +25,14 @@
transform_classes_and_methods_to_r8_startup_descriptors(
profile_classes_and_methods, options)
write_tmp_startup_descriptors(current_startup_descriptors, iteration, options)
- number_of_new_startup_descriptors = add_r8_startup_descriptors(
+ new_startup_descriptors = add_r8_startup_descriptors(
startup_descriptors, current_startup_descriptors)
+ number_of_new_startup_descriptors = len(new_startup_descriptors) - len(startup_descriptors)
if options.out is not None:
print(
'Found %i new startup descriptors in iteration %i'
% (number_of_new_startup_descriptors, iteration + 1))
- return number_of_new_startup_descriptors
+ return new_startup_descriptors
def generate_startup_profile(options):
logcat = None
@@ -96,7 +97,7 @@
if message.startswith('START') \
or message.startswith('Activity pause timeout for') \
or message.startswith('Activity top resumed state loss timeout for') \
- or message.startswith('Force removing')
+ or message.startswith('Force removing') \
or message.startswith(
'Launch timeout has expired, giving up wake lock!'):
continue
@@ -166,37 +167,61 @@
for class_or_method in classes_and_methods:
descriptor = class_or_method.get('descriptor')
flags = class_or_method.get('flags')
- if flags.get('conditional_startup') \
- and not options.include_conditional_startup:
- continue
- if flags.get('post_startup') \
- and not flags.get('startup') \
- and not options.include_post_startup:
- continue
- startup_descriptors.append(descriptor)
+ if should_include_startup_descriptor(descriptor, flags, options):
+ startup_descriptors.append(descriptor)
return startup_descriptors
-def add_r8_startup_descriptors(startup_descriptors, startup_descriptors_to_add):
- previous_number_of_startup_descriptors = len(startup_descriptors)
- if previous_number_of_startup_descriptors == 0:
+def add_r8_startup_descriptors(old_startup_descriptors, startup_descriptors_to_add):
+ new_startup_descriptors = {}
+ if len(old_startup_descriptors) == 0:
for startup_descriptor, flags in startup_descriptors_to_add.items():
- startup_descriptors[startup_descriptor] = flags.copy()
+ new_startup_descriptors[startup_descriptor] = flags.copy()
else:
+ # Merge the new startup descriptors with the old descriptors in a way so
+ # that new startup descriptors are added next to the startup descriptors
+ # they are close to in the newly generated list of startup descriptors.
+ startup_descriptors_to_add_after_key = {}
+ startup_descriptors_to_add_in_the_end = {}
+ closest_seen_startup_descriptor = None
for startup_descriptor, flags in startup_descriptors_to_add.items():
- if startup_descriptor in startup_descriptors:
- # Merge flags.
- existing_flags = startup_descriptors[startup_descriptor]
- assert not flags['conditional_startup']
- if flags['post_startup']:
- existing_flags['post_startup'] = True
+ if startup_descriptor in old_startup_descriptors:
+ closest_seen_startup_descriptor = startup_descriptor
else:
- # Add new startup descriptor.
- new_flags = flags.copy()
- new_flags['conditional_startup'] = True
- startup_descriptors[startup_descriptor] = new_flags
- new_number_of_startup_descriptors = len(startup_descriptors)
- return new_number_of_startup_descriptors \
- - previous_number_of_startup_descriptors
+ if closest_seen_startup_descriptor is None:
+ # Insert this new startup descriptor in the end of the result.
+ startup_descriptors_to_add_in_the_end[startup_descriptor] = flags
+ else:
+ # Record that this should be inserted after
+ # closest_seen_startup_descriptor.
+ pending_startup_descriptors = \
+ startup_descriptors_to_add_after_key.setdefault(
+ closest_seen_startup_descriptor, {})
+ pending_startup_descriptors[startup_descriptor] = flags
+ for startup_descriptor, flags in old_startup_descriptors.items():
+ # Merge flags if this also exists in startup_descriptors_to_add.
+ if startup_descriptor in startup_descriptors_to_add:
+ merged_flags = flags.copy()
+ other_flags = startup_descriptors_to_add[startup_descriptor]
+ assert not other_flags['conditional_startup']
+ if other_flags['post_startup']:
+ merged_flags['post_startup'] = True
+ new_startup_descriptors[startup_descriptor] = merged_flags
+ else:
+ new_startup_descriptors[startup_descriptor] = flags.copy()
+ # Flush startup descriptors that followed this item in the new trace.
+ if startup_descriptor in startup_descriptors_to_add_after_key:
+ pending_startup_descriptors = \
+ startup_descriptors_to_add_after_key[startup_descriptor]
+ for pending_startup_descriptor, pending_flags \
+ in pending_startup_descriptors.items():
+ new_startup_descriptors[pending_startup_descriptor] = \
+ pending_flags.copy()
+ # Insert remaining new startup descriptors in the end.
+ for startup_descriptor, flags \
+ in startup_descriptors_to_add_in_the_end.items():
+ assert startup_descriptor not in new_startup_descriptors
+ new_startup_descriptors[startup_descriptor] = flags.copy()
+ return new_startup_descriptors
def write_tmp_binary_artifact(artifact, iteration, options, name):
if not options.tmp_dir:
@@ -257,6 +282,16 @@
result += startup_descriptor
return result
+def should_include_startup_descriptor(descriptor, flags, options):
+ if flags.get('conditional_startup') \
+ and not options.include_conditional_startup:
+ return False
+ if flags.get('post_startup') \
+ and not flags.get('startup') \
+ and not options.include_post_startup:
+ return False
+ return True
+
def parse_options(argv):
result = argparse.ArgumentParser(
description='Generate a perfetto trace file.')
@@ -329,7 +364,9 @@
iteration = 0
stable_iterations = 0
while True:
- diff = extend_startup_descriptors(startup_descriptors, iteration, options)
+ old_startup_descriptors = startup_descriptors
+ startup_descriptors = extend_startup_descriptors(old_startup_descriptors, iteration, options)
+ diff = len(startup_descriptors) - len(old_startup_descriptors)
if diff == 0:
stable_iterations = stable_iterations + 1
if stable_iterations == options.until_stable_iterations:
@@ -339,15 +376,17 @@
iteration = iteration + 1
else:
for iteration in range(options.iterations):
- extend_startup_descriptors(startup_descriptors, iteration, options)
+ startup_descriptors = extend_startup_descriptors(startup_descriptors, iteration, options)
if options.out is not None:
with open(options.out, 'w') as f:
for startup_descriptor, flags in startup_descriptors.items():
- f.write(startup_descriptor_to_string(startup_descriptor, flags))
- f.write('\n')
+ if should_include_startup_descriptor(startup_descriptor, flags, options):
+ f.write(startup_descriptor_to_string(startup_descriptor, flags))
+ f.write('\n')
else:
for startup_descriptor, flags in startup_descriptors.items():
- print(startup_descriptor_to_string(startup_descriptor, flags))
+ if should_include_startup_descriptor(startup_descriptor, flags, options):
+ print(startup_descriptor_to_string(startup_descriptor, flags))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))