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: I21d317aa155cca76f80ea76a12a9ca82f3aa62e9
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index fbcd34c..2efe014 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 d7fb9f8..1636c56 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) {
assert !options.minimalMainDex :
"Cannot combine package distribution definition with minimal-main-dex option.";
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 9527a97..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,8 +10,8 @@
public static final int ANDROID_O_API = 26;
public static final int ANDROID_N_API = 24;
- public static final int ANDROID_K_API = 19;
public static final int ANDROID_L_API = 21;
+ public static final int ANDROID_K_API = 19;
public static final int DEFAULT_ANDROID_API = 1;
/** dex file version number for Android O (API level 26) */
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 55511fd..0e89d4a 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -325,6 +325,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..d1e46f3 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_L_API) // Allow native multidex.
.setProguardMapFile(proguardMap)
.setPackageDistributionFile(packageMap)
.build();
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 2841779..d5e66b8 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ArtErrorParser;
@@ -70,6 +71,7 @@
outputApp = ToolHelper.runR8(builder.build(),
options -> {
options.printSeeds = false;
+ options.minApiLevel = Constants.ANDROID_L_API;
});
} else {
assert compiler == CompilerUnderTest.D8;
@@ -78,6 +80,7 @@
D8Command.builder()
.addProgramFiles(ListUtils.map(inputs, Paths::get))
.setMode(mode)
+ .setMinApiLevel(Constants.ANDROID_L_API)
.build());
}
Path out = temp.getRoot().toPath().resolve("all.zip");
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index 755fcab..74f9ef9 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.Resource;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.ir.conversion.CallGraph;
import com.android.tools.r8.shaking.ProguardRuleParserException;
@@ -43,6 +44,7 @@
options.testing.irOrdering = this::shuffle;
// Only use one thread to process to process in the order decided by the callback.
options.numberOfThreads = 1;
+ options.minApiLevel = Constants.ANDROID_L_API;
});
}
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
index b6d3dac..376ccf3 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreFixedPointTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import java.io.IOException;
@@ -21,14 +22,18 @@
throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
// First compilation.
AndroidApp app = AndroidApp.fromProgramDirectory(Paths.get(GMSCORE_V7_DIR));
- AndroidApp app1 = ToolHelper.runR8(app);
+ AndroidApp app1 =
+ ToolHelper.runR8(app, options -> options.minApiLevel = Constants.ANDROID_L_API);
// Second compilation.
// Add option --skip-outline-opt for second compilation. The second compilation can find
// additional outlining opportunities as member rebinding from the first compilation can move
// methods.
// See b/33410508 and b/33475705.
- AndroidApp app2 = ToolHelper.runR8(app1, options -> options.outline.enabled = false);
+ AndroidApp app2 = ToolHelper.runR8(app1, options -> {
+ options.outline.enabled = false;
+ options.minApiLevel = Constants.ANDROID_L_API;
+ });
// TODO: Require that the results of the two compilations are the same.
assertEquals(
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 c49b66d..a0a77d1 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -396,6 +396,7 @@
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) {