Check dex index overflow when running mono dex
Mono dex is enforced when the 3 following conditions are met:
- min API is =< 20.
- no main dex list was provided.
- no main dex rules was provided.
Bug: 62482021
Change-Id: If17bba6cd2b6b8517e1eb85c937283eddb49585c
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7dd2d33..dd30160 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -207,7 +207,6 @@
RootSet rootSet;
byte[] proguardSeedsData = null;
timing.begin("Strip unused code");
- Set<DexType> mainDexBaseClasses = null;
try {
Set<DexType> missingClasses = appInfo.getMissingClasses();
missingClasses = filterMissingClasses(missingClasses, options.dontWarnPatterns);
@@ -281,7 +280,7 @@
// Lets find classes which may have code executed before secondary dex files installation.
RootSet mainDexRootSet =
new RootSetBuilder(application, appInfo, options.mainDexKeepRules).run(executorService);
- mainDexBaseClasses = enqueuer.traceMainDex(mainDexRootSet, timing);
+ Set<DexType> mainDexBaseClasses = enqueuer.traceMainDex(mainDexRootSet, timing);
// Calculate the automatic main dex list according to legacy multidex constraints.
// Add those classes to an eventual manual list of classes.
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 7476792..3638c01 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.dex.VirtualFile.FilePerClassDistributor;
import com.android.tools.r8.dex.VirtualFile.FillFilesDistributor;
import com.android.tools.r8.dex.VirtualFile.PackageMapDistributor;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -124,6 +125,16 @@
assert packageDistribution == null :
"Cannot combine package distribution definition with file-per-class option.";
distributor = new FilePerClassDistributor(this);
+ } else if (options.minApiLevel < Constants.ANDROID_L_API
+ && options.mainDexKeepRules.isEmpty()
+ && application.mainDexList.isEmpty()) {
+ if (packageDistribution != null) {
+ throw new CompilationError("Cannot apply package distribution. Multidex is not"
+ + " supported with API level " + options.minApiLevel +"."
+ + " For API level < " + Constants.ANDROID_L_API + ", main dex classes list or"
+ + " rules must be specified.");
+ }
+ distributor = new VirtualFile.MonoDexDistributor(this);
} else if (packageDistribution != null) {
distributor = new PackageMapDistributor(this, packageDistribution, executorService);
} else {
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index 201fc82..3ea63bb 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -10,6 +10,7 @@
public static final int ANDROID_O_API = 26;
public static final int ANDROID_N_API = 24;
+ public static final int ANDROID_L_API = 21;
public static final int ANDROID_K_API = 19;
public static final int DEFAULT_ANDROID_API = 1;
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 3db0502..95fc27a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -309,6 +309,27 @@
}
}
+ public static class MonoDexDistributor extends DistributorBase {
+ public MonoDexDistributor(ApplicationWriter writer) {
+ super(writer);
+ }
+
+ @Override
+ public Map<Integer, VirtualFile> run() throws ExecutionException, IOException {
+ VirtualFile mainDexFile = new VirtualFile(0, writer.namingLens);
+ nameToFileMap.put(0, mainDexFile);
+
+ for (DexProgramClass programClass : classes) {
+ mainDexFile.addClass(programClass);
+ if (mainDexFile.isFull()) {
+ throw new CompilationError("Cannot fit all classes in a single dex file.");
+ }
+ }
+ mainDexFile.commitTransaction();
+ return nameToFileMap;
+ }
+ }
+
public static class PackageMapDistributor extends DistributorBase {
private final PackageDistribution packageDistribution;
private final ExecutorService executorService;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index baaf7d0..c3f2d08 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -166,7 +166,7 @@
private boolean isInMainDexList(DexType iface) {
ImmutableSet<DexType> list = converter.application.mainDexList;
- return list != null && list.contains(iface);
+ return list.contains(iface);
}
// Represent a static interface method as a method of companion class.
diff --git a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
index 376577c..9fcf8c2 100644
--- a/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
+++ b/src/test/java/com/android/tools/r8/dex/ExtraFileTest.java
@@ -47,6 +47,7 @@
R8Command.builder()
.addProgramFiles(original)
.setOutputPath(out)
+ .setMinApiLevel(Constants.ANDROID_N_API) // Allow native multidex.
.setProguardMapFile(proguardMap)
.setPackageDistributionFile(packageMap)
.build();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index f540bc0..fe9caab 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -111,7 +111,8 @@
if (!verifyApplications && !regenerateApplications) {
return;
}
- AndroidApp generated = generateApplication(TWO_LARGE_CLASSES, MAX_METHOD_COUNT);
+ AndroidApp generated =
+ generateApplication(TWO_LARGE_CLASSES, Constants.ANDROID_N_API, MAX_METHOD_COUNT);
if (regenerateApplications) {
generated.write(getTwoLargeClassesAppPath(), OutputMode.Indexed, true);
} else {
@@ -126,7 +127,7 @@
if (!verifyApplications && !regenerateApplications) {
return;
}
- AndroidApp generated = generateApplication(MANY_CLASSES, 1);
+ AndroidApp generated = generateApplication(MANY_CLASSES, Constants.DEFAULT_ANDROID_API, 1);
if (regenerateApplications) {
generated.write(getManyClassesAppPath(), OutputMode.Indexed, true);
} else {
@@ -368,10 +369,11 @@
}
}
- private static AndroidApp generateApplication(List<String> classes, int methodCount)
+ private static AndroidApp generateApplication(List<String> classes, int minApi, int methodCount)
throws IOException, ExecutionException {
Timing timing = new Timing("MainDexListTests");
InternalOptions options = new InternalOptions();
+ options.minApiLevel = minApi;
DexItemFactory factory = options.itemFactory;
DexApplication.Builder builder = new DexApplication.Builder(factory, timing);
for (String clazz : classes) {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
index 9ad745f..ef4af5b 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
+++ b/src/test/java/com/android/tools/r8/maindexlist/two-large-classes.zip
Binary files differ