Add DEX version V40 as experimental
Bug: b/249922554
Change-Id: I6f418812ef43c79922f801ef975a7e28839c83c3
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index a6f6a52..0a679e8 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -784,13 +784,19 @@
dest.forward(size * Constants.TYPE_MAP_LIST_ITEM_SIZE);
}
+ private byte[] dexVersionBytes() {
+ if (options.testing.dexContainerExperiment) {
+ return DexVersion.V40.getBytes();
+ }
+ return options.testing.forceDexVersionBytes != null
+ ? options.testing.forceDexVersionBytes
+ : DexVersion.getDexVersion(options.getMinApiLevel()).getBytes();
+ }
+
private void writeHeader(Layout layout) {
dest.moveTo(0);
dest.putBytes(Constants.DEX_FILE_MAGIC_PREFIX);
- dest.putBytes(
- options.testing.forceDexVersionBytes != null
- ? options.testing.forceDexVersionBytes
- : DexVersion.getDexVersion(options.getMinApiLevel()).getBytes());
+ dest.putBytes(dexVersionBytes());
dest.putByte(Constants.DEX_FILE_MAGIC_SUFFIX);
// Leave out checksum and signature for now.
dest.moveTo(Constants.FILE_SIZE_OFFSET);
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index cd7460b..69c9714 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -95,6 +95,8 @@
return AndroidApiLevel.O;
case V39:
return AndroidApiLevel.P;
+ case V40:
+ return AndroidApiLevel.ANDROID_PLATFORM;
default:
throw new Unreachable();
}
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 492c87b..cdac93d 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -10,10 +10,11 @@
* Android dex version
*/
public enum DexVersion {
- V35(35, new byte[]{'0', '3', '5'}),
- V37(37, new byte[]{'0', '3', '7'}),
- V38(38, new byte[]{'0', '3', '8'}),
- V39(39, new byte[]{'0', '3', '9'});
+ V35(35, new byte[] {'0', '3', '5'}),
+ V37(37, new byte[] {'0', '3', '7'}),
+ V38(38, new byte[] {'0', '3', '8'}),
+ V39(39, new byte[] {'0', '3', '9'}),
+ V40(40, new byte[] {'0', '4', '0'});
private final int dexVersion;
@@ -94,15 +95,23 @@
return Optional.of(V38);
case 39:
return Optional.of(V39);
+ case 40:
+ return Optional.of(V40);
default:
return Optional.empty();
}
}
public static Optional<DexVersion> getDexVersion(char b0, char b1, char b2) {
- if (b0 != '0' || b1 != '3' || b2 < '5' || '9' < b2) {
+ if (b0 != '0') {
return Optional.empty();
}
- return getDexVersion(100 * (b0 - '0') + 10 * (b1 - '0') + (b2 - '0'));
+ for (DexVersion candidate : DexVersion.values()) {
+ assert candidate.getBytes()[0] == '0';
+ if (candidate.getBytes()[2] == b2 && candidate.getBytes()[1] == b1) {
+ return Optional.of(candidate);
+ }
+ }
+ return Optional.empty();
}
}
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 6bf0a8f..ebd5a13 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -153,6 +153,7 @@
public static final int SUPPORTED_DEX_VERSION =
AndroidApiLevel.LATEST.getDexVersion().getIntValue();
+ public static final int EXPERIMENTAL_DEX_VERSION = DexVersion.V40.getIntValue();
public static final int ASM_VERSION = Opcodes.ASM9;
@@ -1879,6 +1880,8 @@
private boolean hasReadCheckDeterminism = false;
private DeterminismChecker determinismChecker = null;
public boolean usePcEncodingInCfForTesting = false;
+ public boolean dexContainerExperiment =
+ System.getProperty("com.android.tools.r8.dexContainerExperiment") != null;
// Testing options to analyse locality of items in DEX files when they are generated.
public boolean calculateItemUseCountInDex = false;
diff --git a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
index f7be6ca..b6f5848 100644
--- a/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
+++ b/src/test/java/com/android/tools/r8/FailCompilationOnFutureVersionsTest.java
@@ -22,7 +22,7 @@
public class FailCompilationOnFutureVersionsTest extends TestBase {
static final int UNSUPPORTED_CF_VERSION = InternalOptions.SUPPORTED_CF_VERSION.major() + 1;
- static final int UNSUPPORTED_DEX_VERSION = InternalOptions.SUPPORTED_DEX_VERSION + 1;
+ static final int UNSUPPORTED_DEX_VERSION = InternalOptions.EXPERIMENTAL_DEX_VERSION + 1;
private final TestParameters parameters;
@@ -68,6 +68,40 @@
}
@Test
+ public void testExperimentatDexVersion() throws CompilationFailedException, IOException {
+ // Generate a DEX file with a version higher than the supported one.
+ Path out =
+ testForD8()
+ .addProgramClasses(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ options ->
+ options.testing.forceDexVersionBytes =
+ ("0" + InternalOptions.EXPERIMENTAL_DEX_VERSION).getBytes())
+ .compile()
+ .writeToZip();
+ try {
+ testForD8()
+ .addProgramFiles(out)
+ .setMinApi(parameters.getApiLevel())
+ .compileWithExpectedDiagnostics(
+ diagnotics -> {
+ diagnotics.assertOnlyErrors();
+ diagnotics.assertErrorsCount(1);
+ assertThat(
+ diagnotics.getErrors().get(0).getDiagnosticMessage(),
+ containsString(
+ "Dex file with version '"
+ + InternalOptions.EXPERIMENTAL_DEX_VERSION
+ + "' cannot be used with min sdk level "));
+ });
+ } catch (CompilationFailedException e) {
+ return;
+ }
+ fail("Expected compilation error");
+ }
+
+ @Test
public void testCf() {
try {
testForD8()